典型例子
兩個水桶分別可以裝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;
}
結束語
其實這個問題並不難。通過對於例子的觀察,自己的判斷,很容易發現規律,總結規律並用代碼實現就可以了。切忌空想不動手。