题目:
倒水问题 “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列开始,不应该有空行或任何尾随空格
解题思路:
倒水问题是一个隐式图问题。对于本题来说,A,B两杯子中的水的状态总共有6种转移方式:
1.倒满A。
2.倒满B。
3.倒空A。
4.倒空B。
5.将A中的水倒入B,直到B被倒满或者A倒空。
6.将B中的水倒入A,直到A被倒满或者B倒空。
我们从一个状态,只能转移到这六个状态中的一个,而且转移到的状态之前不能出现过(如果又转移回到了已经出现过的状态,那么这几步我们倒水的结果是无用功)。这样来看,我们可以用BFS算法进行搜索。为了标记已经到达的状态,我使用了map容器,将每一个到达的状态都装到这个map里面。这样,当我们判断一个状态是否已经到达过时,我们可以搜索这个map中有没有这个元素。然后是结果的输出。这里的输出要求输出每一步的具体的操作步骤,而且还有固定的输出格式。为了输出,我用另一个map存储新状态是从哪个状态转移过来的。这样可以从结果向前反推,从而知道具体的倒水过程。在第一个map中,我将该map的element值定义为int,并规定1,2,3,4,5,6分别对应迁移的六个操作。这样,在输出的时候,就可以找到从开始到最终结果的倒水过程了。
代码:
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <queue>
using namespace std;
int A,B,C;
struct Status {
int x, y;
Status() {}
Status(int _x, int _y) {
x = _x;
y = _y;
}
bool operator<(const Status &b) const {
return x == b.x ? y < b.y : x < b.x;
}
bool operator==(const Status &b) const {
return x == b.x && y == b.y;
}
Status AToB() {
Status c;
c.x = max(x + y - B, 0);
c.y = min(B, x + y);
return c;
}
Status BToA() {
Status c;
c.y = max(x + y - A, 0);
c.x = min(A, x + y);
return c;
}
Status FillA() {
return Status(A, y);
}
Status FillB() {
return Status(x, B);
}
Status EmptyA() {
return Status(0, y);
}
Status EmptyB() {
return Status(x, 0);
}
};
map<Status, int> mp;
map<Status, Status> from;
queue<Status> q;
stack<int> s;
void check(Status x, Status y, int a) {
if (mp.find(x) == mp.end()) {
mp[x] = a;
from[x] = y;
q.push(x);
}
}
void print(Status t)
{
Status it=t;
while(from.find(it) != from.end())
{
s.push(mp[it]);
it=from[it];
}
while(!s.empty())
{
int i;
i=s.top();
switch (i)
{
case 1: cout<<"pour A B"<<endl;break;
case 2: cout<<"pour B A"<<endl;break;
case 3: cout<<"fill A"<<endl;break;
case 4: cout<<"fill B"<<endl;break;
case 5: cout<<"empty A"<<endl;break;
case 6: cout<<"empty B"<<endl;break;
default: ;
}
s.pop();
}
cout<<"success"<<endl;
}
void bfs(int a, int b) {
Status t(a, b);
q.push(t);
mp[t] = -1;
while (!q.empty()) {
t = q.front();
q.pop();
if (t.x == C || t.y == C) {
print(t);
return;
}
check(t.AToB(), t, 1);
check(t.BToA(), t, 2);
check(t.FillA(), t, 3);
check(t.FillB(), t, 4);
check(t.EmptyA(), t, 5);
check(t.EmptyB(), t, 6);
}
}
int main()
{
while(cin>>A>>B>>C)
{
mp.clear();
from.clear();
while(!s.empty()) s.pop();
while(!q.empty()) q.pop();
bfs(0,0);
}
return 0;
}
总结:
挺有意思的一道题。我也是第一次接触这种隐式图问题,用BFS解决感觉还是很新鲜的。总之,这类隐式图问题的关键还是一个问题转化的过程。如何将一个隐式图问题的过程转化为状态转移是解决这类问题的核心。之后的BFS其实不算太难。另外,这个题也加深了我对map这个C++容器的认识。