【C++】給定兩個沒有刻度的容器,對於任意給定的容積,求出如何只用兩個瓶裝出L升的水

典型例子

兩個水桶分別可以裝3L水和5L水,沒有刻度,請問怎麼裝出4L的水。
解答
設A爲小桶,B爲大桶。
則過程中的每個狀態可以如下表示:
方法一:
A B
0 5
3 2
0 2
2 0
2 5
3 4
此時B中裝了4L水,共計6步

方法二:
A B
3 0
0 3
3 3
1 5
1 0
0 1
3 1
0 4
此時B中裝了4L水,共計8步。

現然方法一比方法二可以更快得到結果。這兩者的區別其實就在於:
方法一 是剛開始的時候,大桶裝滿,倒到小桶裏面,方法二是小桶裝滿倒入大桶裏面。也就是一個正逆序的問題。

思路

通過上面的例子,我們可以瞭解到這看似是兩個方案。但是卻只是一個正序和一個逆序罷了。
那麼我們可以求出一個通解嗎?
下面我們再來看上面那個例子。
按照固定的思路
1.假設A桶小,B桶大,且小桶可裝m升水,大桶可裝M升水。
2.我們一般情況下,肯定是把水裝到大桶裏面去。例如4L的水,肯定不能裝到3L的桶裏面去,必須裝入5L的桶裏面去。因此我們可以考慮,先把小桶裝滿,往大桶裏面倒。
開始倒:
最初A和B中裝的水都是0L,這是兩個空桶。

0 0
A B
3 0 //A先裝滿(A=A+3)
0 3 //A中的水倒入B中(B=B+A,B小於5,則A=0;B大於等於5,則A=B-5)
3 3 //A裝滿(A=A+3)
1 5 //此時重複第二行
1 0 //將B中的水倒出(將B置零)
0 1 //A倒入B中(A和B互換)
3 1 //重複第二行
0 4
3 4 //此時B中已經有4L的水了。這上面便是方法二。此時不要停止,繼續倒水
2 5
2 0 //重複第二行
0 2
3 2
0 5
//從最開始A=3.B=0,到了現在變成了A=0,B=5;完成了一個輪迴。

我們如果把下面的這個粗體數字逆序來看看,發現這正是方法一
這裏的3就是小桶容積m,5就是大桶容積M。
這是偶然的嗎?
我們繼續驗證一下。

例子2:有一個2L的桶和6L的桶,如何裝出4L的水?
此時m=2;M=6;
按照我們之前的思路:
A B
2 0 //A=A+m
0 2 //B=B+A,B小於M,則A=0;B大於等於M,則A=B-M
2 2
0 4 //此時已經得出4L的水了。不要停止,繼續倒水。
2 4
0 6
//結束。

通過上面兩個例子,我們發現了其算法的思路:
本質上都是在不斷重複第二行的算法。
通過上面的例子,我們可以清晰的看到,正序和逆序的算法步驟的數量是不相等的,而我們肯定是想要次數最少的步驟,於是可以把上下兩部分的步驟次數進行一次對比,然後輸出次數最少的一種
注意:這裏並不是說明,逆序就是最短的,這裏跟用要求稱出的的水的體積有關。

代碼實現:

#include"iostream"
#include"algorithm"
#include"vector"
using namespace std;


void output(vector<int> a,vector<int> b,int L)//輸出函數,用來輸出結果
{
	vector<int>::iterator ita, itb ,it;
	for (it = b.begin(); it < b.end(); it++)
	{
		if (*it == L)
			break;
	}
	cout << "A" << "\t" << "B" << endl;
	if (b.end() - it - 1 > it - b.begin() + 1)//判斷是後半部分長還是前半部分更長。
	{	
		ita = a.begin();
		itb = b.begin();
		while (itb <= it)
		{

			cout << *ita << "\t" << *itb << endl;
			ita++; itb++;
		}
	}
	else
	{
		for (it = b.end()-1; it > b.begin(); it--)
		{
			if (*it == L)
				break;
		}
		ita = a.end()-1;
		itb = b.end()-1;
		while (itb >= it)
		{
			cout << *ita << "\t" << *itb << endl;
			ita--; itb--;
		}
	}
	cout << "B中裝了" << L << "升水" << endl;
}

void Bucket_problem()
{
	int A, B, L;
	vector<int> A1, B1;//用兩個向量保存兩條數據,到時候容易比較長短和輸出
	int flag = 0;
	cout << "請輸入兩個容器的體積(正整數):" << endl;
	cin >> A >> B;
	cout << "請輸入想要裝出的水的體積(正整數):" << endl;
	cin >> L;
	cout << "此時A爲小桶,B爲大桶" << endl;
	int M = max(A, B);
	int m = min(A, B);
	if (L > M)
	{
		cout << "No Solution" << endl;
		return ;
	}
	if (L == m)
	{
		cout << "A" << "\t" << "B" << endl;
		cout << m << "\t" << "0" << endl;
		cout << "此時A中裝了" << m << "升水" << endl;
		return ;
	}
	if (L == M)
	{
		cout << "A" << "\t" << "B" << endl;
		cout << "0" << "\t" << M << endl;
		cout << "此時B中裝了" << M << "升水" << endl;
		return ;
	}

	int a = 0, b = 0;
	a = m;
	while (true)//製造一個循環
	{
		if (b == M)
		{
			b = 0;
			if (a == 0 && b == 0) break;//退出條件
			A1.push_back(a); B1.push_back(b);
			swap(a, b);
			A1.push_back(a); B1.push_back(b);
			a = m;
			continue;
		}

		A1.push_back(a); B1.push_back(b);
		//		cout << A << "\t" << B;
		b = b + a;
		if (b >= M)
		{
			a = b - M;
			b = M;
			A1.push_back(a); B1.push_back(b);
		}
		else
		{
			a = 0;
			A1.push_back(a); B1.push_back(b);
			a = m;
		}
	}
		output(A1, B1, L);
}
int main()
{
	Bucket_problem();
	system("pause");
	return 0;
}

結束語

其實這個問題並不難。通過對於例子的觀察,自己的判斷,很容易發現規律,總結規律並用代碼實現就可以了。切忌空想不動手。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章