题意
经典逻辑题 倒水问题-有A,B两个容器,问如何倒出C体积的水
“fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
输入:0 < A <= B 、C <= B <=1000 、A和B互质。
输出要求:每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
会有多组数据(保证数据能够成功倒出C体积的水)
样例
样例输入:
2 7 5
2 7 4
样例输出:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success
思路
可以把两个容器当前水的量当做一种状态(即BFS中 图的一个节点),状态之间的转移只有 1.将A倒空 2.将A加满 3.将B倒空 4.将B加满 5.将A倒入B 6.将B倒入A六种可能(相当于BFS中上下左右的走)。因此我们只需从最初(0,0)的状态开始执行BFS,每次从队列取出一个状态,然后将其6个扩展的状态都进行判断是否到达过,到达过则记录状态的前驱 以及将它放入队列,直到状态变成(C,~ )或( ~,C).然后通过map中记录的前驱来输出方案。
在方案的输出方面,可以根据一个状态的前驱到该状态的特性,来判断是怎么进行的(比如 s–>t 如果 t.a= = s.a&& t.b= = 0 则s到t是进行了 "empty B"操作)
总结
1.状态抽象:找到可以唯一表示一个中间状态的方法,可以用sturct构造状态
2.搜索抽象:找到所有可以由一个状态扩展成另一个状态的途径,并且可以完整的表示出来
3.按照一般的BFS进行搜索
代码
#include<iostream>
#include<math.h.>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
struct node
{
int a;int b;
bool operator <(const node &s) const
{
return a!=s.a?a<s.a:b<s.b;
}
bool operator ==(const node &s) const
{
return a==s.a&&b==s.b;
}
bool operator !=(const node &s) const
{
return a!=s.a||b!=s.b;
}
}pre,final;
queue<node> q;
map<node,node> from;//map映射 from[i]=j 表示 状态i从状态j过来 即 j-->i
void check(node & x)
{
if(from.find(x)==from.end())//find()函数用来寻找是否已存在x 满足if则不存在
{
from[x]=pre;//pre用来记录前驱
q.push(x);
}
}
int A,B,C;
void pour_water()
{
node s,t;
s.a=0;s.b=0;
q.push(s);
from[s]=s;
while(!q.empty())
{
s=q.front();q.pop();
//终点判断条件
if(s.a==C||s.b==C) {final=s;return;}
//对所有状态的遍历
if(s.a>0) //把a里的水倒空
{
t.a=0; t.b=s.b; pre=s;
check(t);
}
if(s.b>0)//倒空b
{
t.b=0; t.a=s.a; pre=s;
check(t);
}
if(s.a!=A)//把a倒满
{
t.a=A; t.b=s.b; pre=s;
check(t);
}
if(s.b!=B)//把b倒满
{
t.b=B; t.a=s.a; pre=s;
check(t);
}
if(s.a!=0&&s.b!=B)//a倒入b
{
if(s.a+s.b<=B)
{
t.a=0; t.b=s.a+s.b; pre=s;
check(t);
}
else
{
t.a=s.a+s.b-B; t.b=B; pre=s;
check(t);
}
}
if(s.a!=A&&s.b!=0)//b倒入a
{
if(s.a+s.b<=A)
{
t.a=s.a+s.b; t.b=0; pre=s;
check(t);
}
else
{
t.a=A; t.b=s.a+s.b-A; pre=s;
check(t);
}
}
}
}
void print()
{
vector<node> v;
for(;from[final]!=final;final=from[final]) v.push_back(final);
v.push_back(final);//把最初状态也放进去
node s,t;
for(int i=v.size()-1;i>0;i--)
{
s=v[i];t=v[i-1];//状态顺序为s-->t
//倒空A
if(t.a==0&&t.b==s.b) cout<<"empty A"<<endl;
//倒空B
if(t.b==0&&t.a==s.a) cout<<"empty B"<<endl;
//倒满A
if(t.a==A&&t.b==s.b) cout<<"fill A"<<endl;
//倒满B
if(t.b==B&&t.a==s.a) cout<<"fill B"<<endl;
//a倒入b
if(t.a+t.b==s.a+s.b&&(t.b==B||t.b==s.a+s.b)) cout<<"pour A B"<<endl;
//b倒入a
if(t.a+t.b==s.a+s.b&&(t.a==A||t.a==s.a+s.b)) cout<<"pour B A"<<endl;
}
cout<<"success"<<endl;
}
int main()
{
while(cin>>A>>B>>C)
{
//每次要把队列和map清空
while(!q.empty()) q.pop();
from.clear();
pour_water();
print();
}
system("pause");
return 0;
}