题目
倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000
、A和B互质。程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。Sample Input
2 7 5 2 7 4
Sample Output
fill B pour B A success fill A pour A B fill A pour A B success
解题思路
首先输入数据有多种,而且事先并没有声明组数,因此需要用scanf("%d%d%d", &a, &b, &c) != EOF
输入数据。每组输入的 A B C 分别表示 A杯容量 B杯容量 需要C单位的水 。我们每一次可以有6种操作:A装满水、B装满水、倒空A杯的水、倒空B杯的水、把A中的水倒进B中 和 把B中的水倒进A中。其中后两种的截至条件是一方倒满或一方倒空。
从初始状态开始有六种可能的操作,其中每一种操作都可能产生有效的操作,即会改变 A B 的值,产生一种新的状态。这又产生了一个问题,就是相同的状态,即 A B 的值可能由不同的操作流程产生,我们应该选取总步骤最少的操作流程。因此我们每产生一种可能的状态,都要判断这个状态是否已经产生过了,如果没有,才将这种新状态和产生它的状态保存下来。这样,我们才能通过产生 C 单位水时 A 和 B 的状态反向推出要产生结果状态的操作流程。
所以我们建立结构体 status 存储 A B 的状态,并重载小于运算符,使得我们可以直接比较结构体的大小。给出一个状态,我们要知道产生这种状态的前一个状态和操作步骤,我们可以通过 map 映射完成相关工作。 在程序中,map <status, status> history
; 是状态到状态的映射,map<status, int> histype
; 是状态到操作类型的映射。发现一种状态时,我们先通过 history.find(n)
判断这种状态之前是否已经产生过了,如果没有,则将这种映射关系加入相应的映射中。
要产生尽量短操作步骤,我们可以使用 BFS 搜索,从状态 { 0,0 } 开始,每次从队列中取出头元素,判断是否已经到达终点,如是调用 outprint 函数,通过递归查找末状态的前序状态,并将它们输出,否则就尝试六种可能的操作,把产生的新状态存储下来并加入到队列中。
程序代码
#include <iostream>
#include<queue>
#include<map>
#include<stdio.h>
using namespace std;
struct status { //存储状态
int a, b;
bool operator < (const status& s) const {
if (a != s.a) return a < s.a;
return b < s.b;
}
};
string M[] = { "success\n","fill A\n","fill B\n","empty A\n","empty B\n","pour A B\n","pour B A\n" }; //操作类型
map<status, status> history; //前后序状态的映射
map<status, int> histype; //操作类型的映射
queue<status> q;
void record(status n, status s, int t) { //检查该状态是否产生过 & 存储状态
if (history.end() != history.find(n)) return;
if (n.a == 0 && n.b == 0) return;
// history.insert(pair<n, s>);
history[n] = s;
histype[n] = t;
q.push(n);
}
void outprint(status st) { //递归输出
if (history.find(st) == history.end()) { //到达递归终点
return;
}
status curr = st;
outprint(history[st]); //递归
cout << M[histype[st]]; //输出
}
void output() { //递归输出
outprint(q.front());
return;
}
void pour(int a, int b, int c) { //模拟倒水操作
status s{ 0,0 }; //初始状态
q.push(s); //建立队列
int type;
while (!q.empty()) { //bfs
s = q.front();
if (s.a == c || s.b == c) { //到达终点
outprint(s); //输出
cout << M[0];
return;
}
if (s.a < a) { //fill a
type = 1;
status n{ a, s.b };
record(n, s, type);
}
if (s.b < b) { //fill b
type = 2;
status n{ s.a, b };
record(n, s, type);
}
if (s.a > 0) { //empty a
type = 3;
status n{ 0, s.b };
record(n, s, type);
}
if (s.b > 0) { //empty b
type = 4;
status n{ s.a, 0 };
record(n, s, type);
}
//pour a b
if (s.a > 0) {
type = 5;
if (s.a + s.b >= b) { //倒满b
status n{ s.a + s.b - b, b };
record(n, s, type);
}
else { //倒空a
status n{ 0, s.a + s.b };
record(n, s, type);
}
}
//pour b a
if (s.b > 0) {
type = 6;
if (s.a + s.b >= a) { //倒满a
status n{ a, s.a + s.b - a };
record(n, s, type);
}
else { //倒空b
status n{ s.a + s.b, 0 };
record(n, s, type);
}
}
}
}
int main()
{
int a, b, c;
while (scanf("%d%d%d", &a, &b, &c) != EOF) { //获取输入
pour(a, b, c);
while (!q.empty()) q.pop(); //清空本次使用的数据
history.erase(history.begin(), history.end());
histype.erase(histype.begin(), histype.end());
}
return 0;
}