一.题目描述
现在拥有两个杯子,容量分别为a和b,要求使用这两个杯子量出c容量的水,并输出两个杯子所做的操作。
详细题目如下:
二.思路概述
使用bfs的思路。两个水杯初始状态都是0,而每一次都可以做六种不同的操作,这些操作会导致两个水杯变成不同的状态,再将这些状态入队列,再次进入循环,直到两个水杯中有一个里面的水量为c。在上述过程中,可使用map数组来记录每一个状态的前状态,便于回溯。
三.各部分作用
1.结构体pour:
a,b分别对应着两个杯子里的水量,操作符重载是为了map函数,六个函数对应着六种状态。
struct pour
{
int a;
int b;//两个杯子里的水量
pour(){}
pour(int x,int y)
{
a=x;
b=y;
}
//操作符重载
pour& operator=(pour& val)
{
a=val.a;
b=val.b;
return *this;
}
bool operator!=(const pour &va) const {
return a!= va.a|| b != va.b ;
}
bool operator<(const pour &va) const {
return a == va.a ? b < va.b : a < va.a;
}
//六种操作对应的函数
pour fillA()
{
return pour(A,b);
}
pour fillB()
{
return pour(a,B);
}
pour emptyA()
{
return pour(0,b);
}
pour emptyB()
{
return pour(a,0);
}
pour AtoB()
{
pour c;
c.a=max(a+b-B,0);
c.b=min(a+b,B);
return c;
}
pour BtoA()
{
pour c;
c.a=min(a+b,A);
c.b=max(a+b-A,0);
return c;
}
};
2.check函数
用来给每一次改变状态后水量入队列和记录前驱状态
void check(pour m,pour n)//用来记录前一个状态
{
if(mps.find(m) == mps.end())
{
sp.push(m);
mps[m]=n;
}
}
3.print部分
根据最后的状态进行回溯,并将这些状态入栈,然后用栈从初状态开始输出结果。
void print(pour t)//pp的a或者b==C
{
while(!(t.a==0&&t.b==0))
{
xp.push(t) ;
pour va=mps[t];
t=va;
}
xp.push(t);
while(xp.size()>1 )
{
pour m=xp.top() ;
xp.pop();
pour n=xp.top() ;
determine(m,n);
}
xp.pop() ;
}
四.注意的问题
1.由于此题可能会做不止一次输入和输出,所以map数组、栈、队列都要注意是否清空,栈和队列没有直接清空的函数,需要手动进行。
2.map函数使用起来很方便,解决了很多问题,但是map函数的使用规则可能不是那么友好,比如在check函数中一定要第一行,确认关键字没有出现过,才插入,不然就会报错。
if(mps.find(m) == mps.end())
{
sp.push(m);
mps[m]=n;
}
3.在回溯这个过程中,我尝试过使用数组和栈来存储进入队列的状态,但是都失败了,这两种方法都不好掌握,结果惨烈,最后使用了map。
五.完整代码
#include<iostream>
#include<map>
#include<queue>
#include<stack>
using namespace std;
int A,B,C;
int nu=0;
struct pour
{
int a;
int b;//两个杯子里的水
pour(){}
pour(int x,int y)
{
a=x;
b=y;
}
pour& operator=(pour& val)
{
a=val.a;
b=val.b;
return *this;
}
bool operator!=(const pour &va) const {
return a!= va.a|| b != va.b ;
}
bool operator<(const pour &va) const {
return a == va.a ? b < va.b : a < va.a;
}
//六种操作对应的函数
pour fillA()
{
return pour(A,b);
}
pour fillB()
{
return pour(a,B);
}
pour emptyA()
{
return pour(0,b);
}
pour emptyB()
{
return pour(a,0);
}
pour AtoB()
{
pour c;
c.a=max(a+b-B,0);
c.b=min(a+b,B);
return c;
}
pour BtoA()
{
pour c;
c.a=min(a+b,A);
c.b=max(a+b-A,0);
return c;
}
};
queue<pour> sp;//队列存储每次状态
stack<pour> xp;//栈
map<pour,pour> mps;
void check(pour m,pour n)//用来记录前一个状态
{
if(mps.find(m) == mps.end())
{
sp.push(m);
mps[m]=n;
}
}
void determine(pour m,pour n)
{
if(n.a==A&&n.b==m.b)
cout<<"fill A"<<endl;
else if(n.b==B&&n.a==m.a)
cout<<"fill B"<<endl;
else if(n.a==0&&n.b==m.b)
cout<<"empty A"<<endl;
else if(n.b==0&&n.a==m.a)
cout<<"empty B"<<endl;
else if((n.a==0&&n.b==m.a+m.b)||(n.b==B&&n.a==m.a+m.b-B))
cout<<"pour A B"<<endl;
else if((n.b==0&&n.a==m.a+m.b)||(n.a==A&&n.b==m.a+m.b-A))
cout<<"pour B A"<<endl;
}
void print(pour t)//pp的a或者b==C
{
while(!(t.a==0&&t.b==0))
{
xp.push(t) ;
pour va=mps[t];
t=va;
}
xp.push(t);
while(xp.size()>1 )
{
pour m=xp.top() ;
xp.pop();
pour n=xp.top() ;
determine(m,n);
}
xp.pop() ;
}
void bfs(pour t)
{
//从0,0开始为初始状态
mps[t]=t;
sp.push(t);
while (!sp.empty())
{
t = sp.front();
sp.pop();
if (t.a == C ||t.b == C) //跳出循环,输出步骤
{
//cout<<111<<endl;
print(t);
while(!sp.empty() )
sp.pop() ;
return;
}
//每次做完六种操作的状态进队列,直到x和y的值有一个为c停止
check(t.AtoB(), t);
check(t.BtoA(), t);
check(t.fillA(), t);
check(t.fillB(), t);
check(t.emptyA(), t);
check(t.emptyB(), t);
//cout<<nu<<endl;
}
//记录每次状态的前一个操作,最后输出所有的操作
}
int main()
{
while(cin>>A)
{
cin>>B>>C;
pour p(0,0);//初始状态,
bfs(p);
cout<<"success"<<endl;
mps.clear() ;
}
return 0;
}