问题
倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
Input
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
Output
你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
问题分析
倒水问题,A和B的水量有不同的状态。可将其抽象为点,则将各种状态看做一个图,题目转化为找出图中到达一点的最短路径问题。
数据结构:定义了一个结构体存储A,B的状态和达到此状态的操作序号。使用map结构记录当前状态与上一个状态。
使用BFS搜索的基本思路为:
从A,B都为空开始,找到这个状态的所有可能到达的状态,这些点就为下一步能到的点。
然后再把所有的下一步能走到的点,再寻找它下一步能走到的点,一直循环重复直到找到终点状态,即最短路径。
然后再把每一步的操作和路径存储,搜索完过后,根据终点不断找父节点,记录其操作并输出
代码实现
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
struct status
{
int a;
int b;
int o;
bool operator < (const status &s) const
{
if(a != s.a) return a<s.a;
else return b<s.b;
}
};
queue<status> q;
map<status,status> from;
void judge(status &s,status& t)
{
if(from.find(t) == from.end())//未曾到达
{
from[t]=s;
q.push(t);
}
}
int main()
{
int A,B,C;
status s,t;
while(scanf("%d%d%d",&A,&B,&C) != EOF)
{
s.a=0;
s.b=0;
s.o=-1;
q.push(s);
bool at=true;
while(at)
{
s = q.front();
q.pop();
if(s.b == C || s.a==C){
at=false;
}
else{
if(s.a < A)
{
t.a=A;
t.b = s.b;
t.o = 0;
judge(s,t);
if(s.b != 0)
{
if(s.a+s.b>=A)
{
t.a=A;
t.b=s.a+s.b-A;
t.o=5;
judge(s,t);
}
else{
t.a = s.a+s.b;
t.b = 0;
t.o=5;
judge(s,t);
}
}
}
if(s.b < B)
{
t.a=s.a;
t.b=B;
t.o=1;
judge(s,t);
if(s.a != 0)
{
if(s.a+s.b>=B)
{
t.a=s.a+s.b-B;
t.b=B;
t.o=4;
judge(s,t);
}
else{
t.a = 0;
t.b = s.a+s.b;
t.o=4;
judge(s,t);
}
}
}
if(s.a>0)
{
t.a=0;
t.b=s.b;
t.o=2;
judge(s,t);
}
if(s.b>0)
{
t.a=s.a;
t.b=0;
t.o=3;
judge(s,t);
}}
}
A:
status ret[100];
int i=1;
ret[0] = s;
while(from.find(s) != from.end() &&!(s.a==0&&s.b==0))
{
s = from[s];
ret[i] = s;
i++;
}
//i = i-2;
for(;i>=0;i--){
switch(ret[i].o)
{
case 0:printf("fill A\n");break;
case 1:printf("fill B\n");break;
case 2:printf("empty A\n");break;
case 3:printf("empty B\n");break;
case 4:printf("pour A B\n");break;
case 5:printf("pour B A\n");break;
}
}
printf("success\n");
}
return 0;
}