题意:
有两个已知容量的杯子,现在要只是用这两个杯子得到特定容量的水,杯子可以倒满水,可以倒空,也可以相互倒水。
输入:
输入包含多组数据。每组数据输入 A, B, C
数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
Sample Input:
2 7 5
2 7 4
输出:
你的程序的输出将由一系列的指令(“fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空)组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
Sample Output:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success
思路:
将每次操作后两个杯子里水的量当作一个状态,记录如何从一个状态转化为下一个状态,这样如果把每个状态想象成一个点,则从一个状态转变为另一个状态即为点与点相连,而状态之间的转变只有六种:倒满A、倒满B、倒空A、倒空B、A倒向B、B倒向A。我们的任务是寻找所有相连的点直到找到题中要求得到的点(即有一杯水容量为要求的容量),即从起始状态进行广度优先搜索(BFS),找到最终状态。之后,根据我们记录的状态之间的转变回溯到起始状态即可找到到达最终状态的途径。
代码:
#include <iostream>
#include<stdio.h>
#include <string>
#include <map>
#include<queue>
using namespace std;
struct Status{
int a;int b;
Status(int _a=0,int _b=0)
{
a=_a;b=_b;
}
bool operator<(const Status&S)const
{
return a!=S.a?a<S.a:b<S.b;
}
};
map<Status,pair<Status,string>> mp;
queue<Status> q;
void print(Status&t)
{
if(mp.find(t)==mp.end()||(t.a==0&&t.b==0))
{
return;
}
pair<Status,string> s=mp[t];
print(s.first);
cout<<s.second<<endl;
}
void judge(Status &s,Status &t,const string&str)
{
if(mp.find(t)==mp.end())
{
mp[t]=pair<Status,string>(s,str);
q.push(t);
}
}
void bfs(int A,int B,int C)
{
Status s,t;
s.a=0,s.b=0;
while(!q.empty())
q.pop();
q.push(s);
while(!q.empty())
{
s=q.front();
q.pop();
if(s.a==C||s.b==C)
{
print(s);
return;
}
if(s.a>0)
{//倒空A
t.a=0;
t.b=s.b;
judge(s,t,"empty A");
}
if(s.b>0)
{//倒空B
t.a=s.a;
t.b=0;
judge(s,t,"empty B");
}
if(s.a!=A)
{//倒满A
t.a=A;
t.b=s.b;
judge(s,t,"fill A");
if(s.b!=0)
{//B倒向A
if(s.b+s.a<=A)
{
t.a=s.a+s.b;
t.b=0;
judge(s,t,"pour B A");
}
else
{
t.a=A;
t.b=s.a+s.b-A;
judge(s,t,"pour B A");
}
}
}
if(s.b!=B)
{//倒满B
t.a=s.a;
t.b=B;
judge(s,t,"fill B");
if(s.a!=0)
{//A倒向B
if(s.a+s.b<=B)
{
t.b=s.a+s.b;
t.a=0;
judge(s,t,"pour A B");
}
else
{
t.b=B;
t.a=s.a+s.b-B;
judge(s,t,"pour A B");
}
}
}
}
}
int main(int argc, char** argv) {
int a,b,c;
while(scanf("%d %d %d",&a,&b,&c)!=EOF)
{
getchar();
bfs(a,b,c);
cout<<"success"<<endl;
mp.clear();
}
return 0;
}