倒水问题:
设大、中、小3个杯子的容量分别为a,b,c,最初只有大杯子装满水,其他两个杯子为空。由于没有刻度,用杯子x给杯子y倒水时必须一直持续到把杯子y倒满或者把杯子x倒空,而且不能中途终止。最少需要多少步才能让某一个杯子中的水有x升呢?打印出来每步操作后各个杯子的水量(0<c<b<a<1000).
分析:
题中问最少多少步,宽度优先搜索(BFS)可以解决这种最短路程或者步数的问题。
把3个杯子的状态想象成一个结点,两个结点只能通过倒一次水到达,用一个队列(其实用的是数组)来保存结点,下标front指向的是当前结点,每次把当前结点能到达的子结点(不能和队列中已有的结点重复)加入到队列尾,然后下标front不断向后移动.
代码:
(使用STL 中集合set)
#include<stdio.h>
#include<string.h>
#include<set>
using namespace std;
const int MAX = 10000;
int st[MAX][4]; //状态数组,所有的状态都保存在这; st[i][3]记录父节点下标
int cup[3]; //三个杯子的容量
int x; //目标量
set<int> vis;
int water(int l,int m,int *buf) //l向m倒水
{
if(buf[l] != 0 && buf[m] != cup[m]) //如果l杯中水不为空并且m中水没满
{
if(buf[l]+buf[m]<=cup[m])//将l杯中的水倒完
{
buf[m] +=buf[l];
buf[l] = 0;
return 1;
}
else //将m杯倒满
{
buf[l] =buf[l] - (cup[m] - buf[m]);
buf[m] = cup[m];
return 1;
}
}
return 0;
}
int insert(int *buf) //如果3个杯子的状态和前面的重复,返回0.否则返回1,并插入集合
{
int v=0;
int i;
for(i=0;i<3;i++)
v =v*10+buf[i]; //将3个杯子的状态看成一个3位数
if(vis.count(v)) return 0;
vis.insert(v);
return 1;
}
void print(int index) //递归打印从根节点到目标节点
{
int i;
if(!index) //如果index等于0,说明已到达根节点
return;
else
{
print(st[index][3]);
for(i = 0;i < 3;i++)
printf("%d ",st[index][i]);
printf("\n");
}
return;
}
int main()
{
int front,rear;
int i,j;
int buf[3]; //暂时保存3个杯子状态的数组
scanf("%d%d%d%d",&cup[0],&cup[1],&cup[2],&x); //输入3个杯子的容量和目标量
if(x>cup[0]||x<0) printf("ERROR\n"); //目标量不能大于最大容量,并能小于0
else
{
st[1][0] = cup[0]; st[1][1] = st[1][2] = 0;//根节点初始化,下标从1开始
st[1][3] = 0; //根节点没有父节点
insert(st[1]);
front = 1;rear = 2;
while(front < rear)
{
if(st[front][0] == x ||st[front][1] == x||st[front][2] == x) //如果已达到目标量,跳出循环
break;
memcpy(buf,st[front],sizeof(buf)); //front中的值赋给buf
//将i杯中的水倒入j杯
for(i = 0;i < 3;i++)
for(j = 0;j < 3;j++)
{
if(i != j)
{
if(water(i,j,buf)) //如果倒水成功
{
if(insert(buf)) //如果没有和前面到达的节点重复
{
memcpy(st[rear],buf,sizeof(buf)); //将buf中新到达的节点加入队列中
st[rear][3] = front; //记录父节点的下标
rear++;
}
memcpy(buf,st[front],sizeof(buf)); //重新赋给buf当前节点的值
}
}
}
front++;
}
if(front < rear) //front<rear,说明找到了存在目标量的节点
{
print(front); //输出每步操作后的各杯子容量
}
else
printf("没有办法让一个杯子中有%d升水\n",x);
}
scanf("%d",&x);
return 0;
}