这一题运用广度优先搜索可以解决,主要是各个状态的转移以及某个状态出现过要标记,避免重复,进入死循环。
下面是AC代码,上面有详细的讲解:
# include <iostream>
# include <cstring>
# include <queue>
using namespace std;
class data //队列的结点,
{
public:
int water[3]; //三个水杯的状态
int step; //步骤
};
bool visited[100][100][100]; //标记三个水杯的状态,避免重复
int start[3], end[3]; //输入的开始状态和结束状态
int main()
{
int BFS();
int n;
cin >> n;
while(n--)
{
cin >> start[0] >> start[1] >> start[2];
cin >> end[0] >> end[1] >> end[2];
int k = BFS();
cout << k << endl;
}
return 0;
}
int Achieve(data node) //判断是否到达结束状态
{
for(int i = 0; i < 3; i++)
{
if(node.water[i] != end[i])
return 0;
}
return 1;
}
int BFS() //广度优先搜索
{
int i, j;
queue <data> Que; //创建队列
memset(visited, false, sizeof(visited)); //初始化visited数组
data s1; //开始结点
s1.water[0] = start[0]; //第一个杯子
s1.water[1] = 0; //第二个杯子
s1.water[2] = 0; //第三个杯子
s1.step = 0; //步数
Que.push(s1); //进队列
visited[start[0]][0][0] = true; //标记该状态出现过
while(!Que.empty()) //判断队列是否为空
{
s1 = Que.front(); //从队列中获取队头
Que.pop();
if(Achieve(s1)) //到达结束状态,返回步数
return s1.step;
for(i = 0; i < 3; i++) //从i号杯子到往另外两个杯子中放水。
{ //i为倒水的杯子,j为接水的杯子
for(j = 0; j < 3; j++)
{
if(j == i) //i=j,杯子相同,只能往另外的杯子倒水
continue;
if(s1.water[i] != 0 && s1.water[j] < start[j]) //倒水的杯子必须有水且接水的杯子水量不能超过容量
{
data node;
node = s1;
int pour = start[j] - node.water[j]; //要倒的水量
if(node.water[i] >= pour) //倒水的杯子的水量大于等于pour,倒水的杯子不会倒空
{
node.water[j] += pour;
node.water[i] -= pour;
}
else //倒空的情况
{
node.water[j] += node.water[i];
node.water[i] = 0;
}
node.step = s1.step + 1; //步数加一
if(!visited[node.water[0]][node.water[1]][node.water[2]]) //判断该状态是否出现过
{
visited[node.water[0]][node.water[1]][node.water[2]] = true;
Que.push(node);
}
}
}
}
}
return -1; //不能实现,返回-1;
}
很容易看得懂的,慢慢理解,细细体会。