题意
输入三个整数A,B,C(A,B<=100并且C<=max(A,B))。
A,B分别代表两个水杯的最大容积。
一开始两个水杯都是空的,你可以进行如下6个操作(题目中说是3种,将所有操作扩展之后是6个操作):
FILL(1):将第1个杯子装满。
FILL(2):将第2个杯子装满。
DROP(1):倒掉第1个杯子里的水。
DROP(2):倒掉第2个杯子里的水。
POUR(1,2):将第1个杯子里的水倒向第2个杯子。
POUR(2,1):将第2个杯子里的水倒向第1个杯子。
你可以反复执行上述操作。
问最少经过多少次操作后两个杯子中至少有一个杯子的水量等于C?
输出最少操作次数并输出具体操作。(具体看样例)
思路
一个非常有趣的题目,和N - 迷宫问题是同一类型的搜索题。
比较难的点还是在于如何找到此步操作的上一步的状态与操作。
首先对于最少操作这个点我们可以先进行一个剪枝,将一些无意义的操作无视掉。
1.当1(2)号杯已经满了,我们不执行1(2)操作。
2.当1(2)号杯已经空了,我们不执行3(4)操作。
3.当1(2)号杯已经满了,我们不执行6(5)操作。
4.当1(2)号杯已经空了,我们不执行5(6)操作。
简而言之,就是不向已经满了的杯子倒水(来源无所谓),不让空杯向外面倒水(倒在哪里无所谓)。
其次就是当我们执行第5种操作与第6种操作时出现的两种情况:
POUR(1,2):将第1个杯子里的水倒向第2个杯子。
POUR(2,1):将第2个杯子里的水倒向第1个杯子。
以5操作举例:当我们将1号杯倒向2号杯时,我们需要考虑:
(1)1号杯中水量大于2号杯中剩余容量时(此时min(v1,b-v2)=b-v2)。
(2)1号杯中水量不大于2号杯中剩余容量时(此时min(v1,b-v2)=v1)。
其中v1代表1号杯中水量,v2代表2号杯中水量,b代表2号杯容量。
那么如果是(1)状态,那么操作之后就应该是
v1 = v1-b+v2 = v1-min(v1 ,b-v2)
v2 = v2+b-v2 = v2+min(v1 ,b-v2)
如果是(2)状态,那么操作之后就是:
v1 = v1 - v1 = v1-min(v1 ,b-v2)
v2 = v2 + v1 = v2+min(v1 ,b-v2)
所以我们得到执行过5操作后v1,v2的表达式:
v1=v1-min(v1 ,b-v2)
v2=v2+min(v1 ,b-v2)
同理,执行6操作之后v1,v2的表达式就应该是(a为1号杯的容量):
v1=v1+min(v2 ,a-v1)
v2=v2-min(v2 ,a-v1)
讲完这些细节我们来看一下主要的部分:如何知道这一步用了哪个操作,又如何知道我从哪一步用了哪个操作才来到了这一步?
首先我们先定义一个int的二维数组:
//maxn=105
int done[maxn][maxn];
done[i][j]的值的含义:当1号杯水量为i,2号杯水量为j时,我们上一步做了哪种操作。
比如样例中初始状态为0 0。
我们执行操作2,状态变为0 5,那么done[0][5]=2。
接下来定义结构体node:
struct node
{
int x,y; //x:当前1号杯的水量 y:当前2号杯的水量
node() {}
node(int xx,int yy):x(xx),y(yy) {}
};
done数组只能帮我们存储操作方式,却无法记录当前状态是从哪个状态转化而来,此时我们创建一个结构体数组from:
struct node
{
int x,y;
node() {}
node(int xx,int yy):x(xx),y(yy) {}
} from[maxn][maxn];
from[i][j]与done[i][j]之间的关系:1号杯水量为x,2号杯水量为y的①号状态,通过done[i][j]的操作之后,变为1号杯水量为i,2号杯水量为j的②号状态。
理解了这两个数组的意义之后接下来A掉这道题就像如履平川一般。
直接放代码,「伊丽莎白」!
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <stdio.h>
#include <stack>
#include <queue>
using namespace std;
const int maxn=105;
struct node
{
int x,y;
node() {}
node(int xx,int yy):x(xx),y(yy) {}
} from[maxn][maxn];
int done[maxn][maxn];
bool vis[maxn][maxn]; //标记数组
int a,b,c,k;
stack<int>s; //用栈存储每一步的操作,两次逆序后顺序就正常了
void BFS()
{
queue<node>q;
memset(vis,false,sizeof(vis));
q.push(node(0,0));
vis[0][0]=true;
while(!q.empty())
{
node tmp=q.front();
q.pop();
if(tmp.x==c||tmp.y==c) //两者之中只要有一者达到C即可
{
while(tmp.x!=0||tmp.y!=0) //只要两个杯子不全为0,那么一定有前置状态以及对应的转化操作
{
s.push(done[tmp.x][tmp.y]);
k=tmp.x;
tmp.x=from[tmp.x][tmp.y].x;
tmp.y=from[k][tmp.y].y; //这个地方由于tmp.x已经变成上一步的状态,所以先设置变量保存当前x的状态
}
cout<<s.size()<<endl;
while(!s.empty()) //对应序号输出对应内容
{
if(s.top()==1)
cout<<"FILL(1)"<<endl;
else if(s.top()==2)
cout<<"FILL(2)"<<endl;
else if(s.top()==3)
cout<<"DROP(1)"<<endl;
else if(s.top()==4)
cout<<"DROP(2)"<<endl;
else if(s.top()==5)
cout<<"POUR(1,2)"<<endl;
else
cout<<"POUR(2,1)"<<endl;
s.pop();
}
return;
}
node tail;
if(tmp.x<a&&!vis[a][tmp.y]) //1号操作
{
tail.x=a;
tail.y=tmp.y;
vis[tail.x][tail.y]=true;
from[tail.x][tail.y].x=tmp.x;
from[tail.x][tail.y].y=tmp.y;
done[tail.x][tail.y]=1;
q.push(tail);
}
if(tmp.y<b&&!vis[tmp.x][b]) //2号操作
{
tail.x=tmp.x;
tail.y=b;
vis[tail.x][tail.y]=true;
from[tail.x][tail.y].x=tmp.x;
from[tail.x][tail.y].y=tmp.y;
done[tail.x][tail.y]=2;
q.push(tail);
}
if(tmp.x>0&&!vis[0][tmp.y]) //3号操作
{
tail.x=0;
tail.y=tmp.y;
vis[tail.x][tail.y]=true;
from[tail.x][tail.y].x=tmp.x;
from[tail.x][tail.y].y=tmp.y;
done[tail.x][tail.y]=3;
q.push(tail);
}
if(tmp.y>0&&!vis[tmp.x][0]) //4号操作
{
tail.x=tmp.x;
tail.y=0;
vis[tail.x][tail.y]=true;
from[tail.x][tail.y].x=tmp.x;
from[tail.x][tail.y].y=tmp.y;
done[tail.x][tail.y]=4;
q.push(tail);
}
if(tmp.x>0&&tmp.y<b&&!vis[tmp.x-min(tmp.x,b-tmp.y)][tmp.y+min(tmp.x,b-tmp.y)]) //5号操作
{
tail.x=tmp.x-min(tmp.x,b-tmp.y);
tail.y=tmp.y+min(tmp.x,b-tmp.y);
vis[tail.x][tail.y]=true;
from[tail.x][tail.y].x=tmp.x;
from[tail.x][tail.y].y=tmp.y;
done[tail.x][tail.y]=5;
q.push(tail);
}
if(tmp.x<a&&tmp.y>0&&!vis[tmp.x+min(tmp.y,a-tmp.x)][tmp.y-min(tmp.y,a-tmp.x)]) //6号操作
{
tail.x=tmp.x+min(tmp.y,a-tmp.x);
tail.y=tmp.y-min(tmp.y,a-tmp.x);
vis[tail.x][tail.y]=true;
from[tail.x][tail.y].x=tmp.x;
from[tail.x][tail.y].y=tmp.y;
done[tail.x][tail.y]=6;
q.push(tail);
}
}
cout<<"impossible"<<endl; //如果无法到达最终状态输出impossible
return;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>a>>b>>c;
BFS();
return 0;
}
这个题也是思考了快一周才想出来,主要是要想出来from和done数组以及其意义。
开心开心~~
下次再见~