三个水杯倒水问题

 

解题思路

对于每个杯子,它都有两种倒水的操作,如A、B、C三个杯子。要倒出A杯中的水,可以倒给B杯也可以倒给C杯。即共有六种倒水的操作。
那么可以得到一个解空间树的题。树的每一个节点便是当前三个杯子中的水量。当一个节点的水量为目的水量的时候就结束。
因为对三个杯子有六种操作,也就是说可能有六种状态,记住只是可能。也就是说每一个节点可最多以衍生六个节点。
而开始状态就是树的根节点。
既然当某一个节点状态与目的状态相同时候可以结束空间树了,那么若是无法达到目的状态如何结束空间树的衍生呢?
可以用宽度优先搜索,因为每次是以一个节点衍生出最多六个节点,每次衍生的时候取出队列里的第一个数开始衍生节点。
把每次衍生的节点再压入队列里面,直到队列为空就停止。
注: 每次衍生节点的时候,还需要判断衍生出来的节点状态是否已经出现过,固然需要一个三维的标记数组。
 空间树的结构自己可以动手去画,每个节点是A、B、C三个杯子的当前水量。

 

对该题详细讲解博客地址:http://blog.csdn.net/code_pang/article/details/7802944

此题代码如下:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int maxn = 110;
  4 int mark[maxn][maxn][maxn];
  5 int InitCup[3], TargetCup[3];
  6 struct Node
  7 {
  8     int state[3];
  9     int step;
 10 };
 11 
 12 //检测节点是否是目的节点
 13 bool checkTarget(Node a);
 14 //倒水操作,Start为要倒给其他杯子水,End为要水的杯子
 15 void pourwater(int Start, int End, Node &cup);
 16 //宽度优先搜索函数
 17 int bfs();
 18 
 19 int main()
 20 {
 21     int tcase;
 22     cin>>tcase;
 23     while(tcase--)
 24     {
 25         for(int i=0; i<3; i++) cin>>InitCup[i];
 26         for(int i=0; i<3; i++) cin>>TargetCup[i];
 27         cout<<bfs()<<endl;
 28     }
 29     return 0;
 30 }
 31 bool checkTarget(Node a)
 32 {
 33     for(int i=0; i<3; i++)
 34     {
 35         if(a.state[i] != TargetCup[i])
 36             return false;
 37     }
 38     return true;
 39 }
 40 
 41 void pourwater(int Start, int End, Node &cup)
 42 {
 43     int needwater = InitCup[End] - cup.state[End];
 44     if(cup.state[Start] > needwater)
 45     {
 46         cup.state[Start] -= needwater;
 47         cup.state[End] = InitCup[End];
 48     }
 49     else{
 50         cup.state[End] += cup.state[Start];
 51         cup.state[Start] = 0;
 52     }
 53     //倒水完后,操作加一
 54     cup.step++;
 55 }
 56 
 57 int bfs()
 58 {
 59     //声明根节点,与状态节点队列
 60     Node root;
 61     queue<Node> State;
 62     memset(mark, 0, sizeof(mark));
 63     //开始的时候,根节点只有第一个杯子有水。
 64     root.state[0] = InitCup[0];
 65     root.state[1] = root.state[2] = 0;
 66     root.step = 0;
 67 
 68     State.push(root);
 69     //标记当前状态,以后衍生出此状态的节点不用再次压入队列
 70     mark[root.state[0]][0][0] = 1;
 71 
 72     while(State.size())
 73     {
 74         Node father = State.front();
 75         State.pop();
 76         if(checkTarget(father))
 77             return father.step;
 78 
 79         //开始根据父节点father衍生六个子节点
 80         for(int Startcup=0; Startcup<3; Startcup++){
 81             for(int step=1; step<3; step++){
 82                 int Endcup = (Startcup+step)%3;
 83                 //临时存储原来的三个杯子状态Cup
 84                 Node Cup = father;
 85                 //判断是否应该倒水,当倒出水的杯子有水且要倒的杯子里水没满时候可以执行
 86                 if( Cup.state[Startcup] != 0 && Cup.state[Endcup] < InitCup[Endcup]){
 87                     //倒水操作
 88                     pourwater(Startcup, Endcup, Cup);
 89 
 90                     //判断当前的状态是否压入过队列中,没有则压入队列中
 91                     if(mark[Cup.state[0]][Cup.state[1]][Cup.state[2]] == 0){
 92                         mark[Cup.state[0]][Cup.state[1]][Cup.state[2]] = 1;
 93                         State.push(Cup);
 94                     }//if-mark
 95                 }//if-Cup
 96             }//for-step
 97         }//for-Start-cup
 98     }//while
 99     return -1;
100 }
View Code

 

 

posted @ 2017-08-16 18:52  无语了的小菜鸟  阅读(144)  评论(0)    收藏  举报