三個桶等分八升水
有這樣一道智力題目:有三個容積分別是3升、5升和8升的水桶,其中容積爲8升的水桶中裝滿了水,容積爲3升和容積爲5升的水桶是空的。3個水桶都沒有體積刻度,現在需要將大水桶中的8升水等分成兩份,每份都是4升水,附加條件是隻能使用另外兩個空水桶,不能借助其他輔助容器。
這是一個很經典的問題,但是並不難,大部分人都可以在一分鐘內給出答案。不過,很多人可能沒有注意到,這個問題的答案不止一個。先來看一個最常見的答案,也是目前已知最快的操作步驟,一共倒水7次。
1)從8升水桶中倒5升水到5升水桶中
2)從5升水桶中倒3升水到3升水桶中
3)從3升水桶中倒3升水到8升水桶中
4)從5升水桶中倒2升水到3升水桶中
5)從8升水桶中倒5升水到5升水桶中
6)從5升水桶中倒1升水到3升水桶中
7)從3升水桶中倒3升水到8升水桶中
最後的結果是5升水桶和8升水桶中各有升水,其實還有很多種答案,我們就不一一列舉了,到底有多少種答案?水從水桶間倒來倒去,情況太多了,我們算不出來,但是計算機可以。設計一個算法,讓計算機幫助我們把所有的答案都找出來,這就是本章的內容。
問題與求解思路
如果用人的思維方式,那麼解決這個問題的關鍵是怎麼通過倒水湊出確定的1升水或能容納1升水的空間,三隻水桶的容積分別是3、5和8,用這三個數做加減運算,可以得到很多組答案,例如:
3 - (5 - 3) = 1
但是計算機並不能理解這個“1”的重要性,很難按照人類的思維方式按部就班地推導答案,因此計算機解決這個問題,通常會用“窮舉法”。爲什麼用“窮舉法”呢?因爲這不是一個典型意義上的求解最優解的問題,雖然可能暗含了求解倒水次數最少的方法的要求,但就本質而言,常用的求解最優解問題的高效方法都不適用於此問題。如果能夠窮舉解空間的全部合法解,然後通過比較找到最優解也是一種求解最優解的方法。
倒水動作的數學模型
一個合法的倒水動作包含三個要素:倒出水的桶、倒入水的桶和倒水體積。我們用一個三元組來描述倒水動作:<font face =“consolas">{from, to, water}from是指從哪個桶中倒水,to是指將水導向哪個桶,water是此次倒水動作所倒的水量。倒水動作的數據結構定義如下。
typedef struct tagACTION{
int from;
int to;
int water;
}ACTION;
包含動作的倒水狀態定義如下:
struct BucketState
{
···
int bucket_s[BUCKET_COUNT];
ACTION cuiAction;
};
狀態樹的遍歷
bool BucketState::CanTakeDumpAction(int from, int to)
{
assert((from >= 0) && (from < BUCKETS_COUNT));
assett((to >= 0) && (to < BUCKETS_COUNT));
if( (from != 0)
&& !IsBucketEmpty(from)
&& !IsBucketEmpty(to) )
{
return true;
}
return false;
}