Title:有两个容量为A,B的空杯,以及不限量的水。经过一系列对A和B的操作使任意一杯的水量为C。操作包括:“fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。对B同理,即共有6种操作。
Input:输入包含多组数据。每组数据输入 A, B, C
数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
Output:你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
样例:
Input:
2 7 5
2 7 4
Output:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success
分析:倒水问题其实可以类比迷宫问题。
- 某一时刻A和B两个杯子的水量等价于迷宫中的某个点坐标;
- 对A和B进行6种操作等价于对迷宫中的某个点进行上下左右的移动;
- 标记A和B的下一个状态是否出现过,等价于标记迷宫中某个点是否已经被访问过,在本次代码中,使用了map<point,point> pre,在记录状态和状态之间的联系的同时,可以通过pre.find(point A)==pre.end()是否为真,来判断状态A是否已经被访问。
- 对于输出一系列的操作,只需要在结构体中增加一个int型成员op,用于记录该状态是由哪一种操作(对6种操作进行编号0-5)形成的,将迷宫问题中的输出点改为输出op对应的字符串即可
需要注意的是:
- 在倒满A杯之前需要判断A杯是否已经满了,已满则不需要进行倒满A的操作;
- 在倒空A杯之前需要判断A杯是否已经是空;已空则不需要进行倒空A的操作;
- 对于B杯也同理。这样可以减少那些不必要的分支,例如一直在倒满A,一直在倒空A。
对6种操作进行编号并输出
1.使用一个一维数组a
a[0]为“fill A”;a[1]为“empty A”,依此类推。只要输出a[op]即可输出相应的操作。
2.使用map
map<int,string> mp;mp.insert({0,“fill A”});输出mp.find(op)->second即可输出相应的操作。
3.递归输出思想与迷宫问题中的输出类似。
代码变量解释:
结构体point用于记录A和B的当前状态,x,y分别表示A和B当前的水量;
#include<iostream>
#include<queue>
#include<map>
#include<string>
using namespace std;
map<int, string> mp;
struct point
{
int x;
int y;
int op;
point(int thex, int they, int theop)
{
x = thex;
y = they;
op = theop;
}
point(int thex, int they)
{
x = thex;
y = they;
op = -1;
}
point()
{
x = 0;
y = 0;
op = -1;
}
bool operator<(const point& p) const
{
return x != p.x ? x < p.x : y < p.y;
}
};
map<point, point> pre;
void output(point p)
{
if ((p.x == 0 && p.y == 0))
return;
output(pre[p]); // 递归
cout << mp.find(p.op)->second << endl;
}
void bfs(int A,int B,int C)
{
point begin(0, 0);
queue<point> que;
//起点入队列
que.push(begin);
while (!que.empty())
{
point now = que.front();
que.pop();
//到达终点
if (now.x == C || now.y == C)
{
output(now);
cout << "success" << endl;
return;
}
//选择下一步,一共6种选择
for (int i = 0; i < 6; i++)
{
if (i == 0)
{
//A没满,倒满A
if (now.x != A)
{
if(pre.find(point(A, now.y, i))==pre.end())//判断有没有走到这一个状态
{
pre[point(A, now.y, i)] = now;
que.push(point(A, now.y, i));
}
}
}
//A没空,倒空A
else if (i == 1)
{
if (now.x != 0)
{
if (pre.find(point(0, now.y, i)) == pre.end())
{
que.push(point(0, now.y, i));
pre[point(0, now.y, i)] = now;
}
}
}
//A倒B,B满或A空
else if (i == 2)
{
//A不空才能倒
if (now.x != 0)
{
//B到满的距离和A到空的距离比较
if (B - now.y <= now.x)//B会倒满
{
if (pre.find(point(now.x + now.y - B, B, i)) == pre.end())
{
que.push(point(now.x + now.y - B, B, i));
pre[point(now.x + now.y - B, B, i)] = now;
}
}
else//A会倒空
{
if (pre.find(point(0, now.x + now.y, i)) == pre.end())
{
que.push(point(0, now.x + now.y, i));
pre[point(0, now.x + now.y, i)] = now;
}
}
}
}
//B没满,倒满B
else if (i == 3)
{
if (now.y != B)
{
if (pre.find(point(now.x, B, i)) == pre.end())
{
que.push(point(now.x, B, i));
pre[point(now.x, B, i)] = now;
}
}
}
//B没空,倒空B
else if (i == 4)
{
if (now.y != 0)
{
if (pre.find(point(now.x, 0, i)) == pre.end())
{
que.push(point(now.x, 0, i));
pre[point(now.x, 0, i)] = now;
}
}
}
//B倒A,A满或B空
else
{
//B不空才能倒
if (now.y != 0)
{
//A到满的距离和B到空的距离比较
if (A - now.x <= now.y)//A会倒满
{
if (pre.find(point(A, now.x + now.y - A, i)) == pre.end())
{
que.push(point(A, now.x + now.y - A, i));
pre[point(A, now.x + now.y - A, i)] = now;
}
}
else//B会倒空
{
if (pre.find(point(now.x + now.y, 0, i)) == pre.end())
{
que.push(point(now.x + now.y, 0, i));
pre[point(now.x + now.y, 0, i)] = now;
}
}
}
}
}
}
}
int main()
{
mp.insert({0,"fill A"});
mp.insert({1,"empty A"});
mp.insert({2,"pour A B"});
mp.insert({3,"fill B" });
mp.insert({4,"empty B" });
mp.insert({5,"pour B A" });
int A=0, B=0, C=0;
while (cin >> A >> B >> C)
{
pre.clear();
bfs(A, B, C);
}
return 0;
}