WEEK2 周记 作业B题——广度优先搜索倒水问题
一、题意
1.简述
倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
2.输入格式
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
3.输出格式
你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
4.样例
Input
Output
注释
如果你的输出与Sample Output不同,那没关系。对于某个"A B C"本题的答案是多解的,不能通过标准的文本对比来判定你程序的正确与否。 所以本题由 SPJ(Special Judge)程序来判定你写的代码是否正确。
二、算法
1.主要步骤
首先这个题不知道有多少组,所以还是老样子需要用scanf("%s",str)!=EOF
来处理。
对于每一组,我们可以通过广搜来不断搜索直到获得某一个杯子的容量是c的状态。
事实上,我们可以将a、b两个杯子内的容量作为坐标点,以此来构建一个图。初始坐标点当然是(0,0),因为初始时杯子是空的。
基于广搜的思想,我们不难发现,这个题与传统广搜的不同之处就是寻找邻接点的方式不同,这里并不是四周的四个点,这个题的有趣之处在于,邻接点的获取是通过一些动作来获得的。那么我们只需要将这些动作转化为图上的坐标的转移,就能解决这个问题了。
为了将这些动作转化为图上的坐标的转移,我为每一个动作都封装了一个函数,这个函数将自动检查传入的当前点在这个动作下获得的邻接点是否是原先已经出现过的状态,如果不是,那么就将这个状态点入队;否则就跳过。
另一点不同之处在于,广搜循环终止的条件除了队列为空以外,还有某一个水杯的容量达到c,所以实际上“终点”并不是一个点,而是两条线。
这样主程序部分其实实现起来就很明了了。
该题还需要注意的是,在某一个点入队之后,需要存储一下这个点是由什么动作入队的,以及他的前一个状态点是谁。这样在找到终点之后,就可以根据他的前一个状态点是谁不断回溯,直到起点。我们就可以轻松的利用递归来逆序输出。
2.测试结果
三、总结与收获
条件语句有时可以通过三目运算符得到简化
比如实现pour A B这一动作的void a2b(int x,int y)
函数的实现。
void a2b(int x,int y)
{
int bb=(x+y>b)? b:x+y;
int aa=(x+y>b)? (x+y-b):0;
//两行三目运算符省略了大量的条件语句
if(m[aa][bb]==0)
{
pair<int,int> cc(aa,bb);
q.push(cc);
m[aa][bb]=1;
dis[aa][bb]=5;
pre[aa][bb].first=x;
pre[aa][bb].second=y;
}
}
四、代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int m[1001][1001];
int dis[1001][1001];
int a,b,c;
queue<pair<int,int> > q;
string cons[7]={
"0","fill A","fill B","empty A","empty B",
"pour A B","pour B A"
};
pair<int,int> pre[1001][1001];
void print(int x,int y)
{
int i;
if(x==0&&y==0)
return;
print(pre[x][y].first,pre[x][y].second);
printf("%s\n",cons[dis[x][y]].c_str());
}
void fill(int x,int y)
{
if(m[a][y]==0)
{
pair<int,int> cc(a,y);
q.push(cc);
m[a][y]=1;
dis[a][y]=1;
pre[a][y].first=x;
pre[a][y].second=y;
}
if(m[x][b]==0)
{
pair<int,int> cc(x,b);
q.push(cc);
m[x][b]=1;
dis[x][b]=2;
pre[x][b].first=x;
pre[x][b].second=y;
}
}
void empty(int x,int y)
{
if(m[0][y]==0)
{
pair<int,int> cc(0,y);
q.push(cc);
m[0][y]=1;
dis[0][y]=3;
pre[0][y].first=x;
pre[0][y].second=y;
}
if(m[x][0]==0)
{
pair<int,int> cc(x,0);
q.push(cc);
m[x][0]=1;
dis[x][0]=4;
pre[x][0].first=x;
pre[x][0].second=y;
}
}
void a2b(int x,int y)
{
int bb=(x+y>b)? b:x+y;
int aa=(x+y>b)? (x+y-b):0;
if(m[aa][bb]==0)
{
pair<int,int> cc(aa,bb);
q.push(cc);
m[aa][bb]=1;
dis[aa][bb]=5;
pre[aa][bb].first=x;
pre[aa][bb].second=y;
}
}
void b2a(int x,int y)
{
int aa=(x+y>a)? a:x+y;
int bb=(x+y>a)? (x+y-a):0;
if(m[aa][bb]==0)
{
pair<int,int> cc(aa,bb);
q.push(cc);
m[aa][bb]=1;
dis[aa][bb]=6;
pre[aa][bb].first=x;
pre[aa][bb].second=y;
}
}
int main()
{
while(scanf("%d%d%d",&a,&b,&c)!=EOF)
{
while(!q.empty())
q.pop();
memset(dis,0,sizeof(dis));
memset(pre,0,sizeof(pre));
memset(m,0,sizeof(m));
pair<int,int> cor(0,0);
q.push(cor);
m[cor.first][cor.second]=1;
int x,y;
while(!q.empty())
{
pair<int,int> temp=q.front();
q.pop();
x=temp.first;
y=temp.second;
empty(x,y);
fill(x,y);
a2b(x,y);
b2a(x,y);
if(x==c||y==c)
break;
}
print(x,y);
printf("success\n");
}
return 0;
}