题目:
给你两个容器,分别能装下 A 升水和 B 升水,并且可以进行以下操作: FILL(i):将第 i 个容器从水龙头里装满(1≤i≤2); DROP(i):将第 i 个容器抽干; POUR(i,j):将第 i 个容器里的水倒入第 j 个容器(这次操作结束后产生两种结果,一是第j 个容器倒满并且第 i 个容器依旧有剩余,二是第 i 个容器里的水全部倒入 j 中,第 i 个容器为空); 现在要求你写一个程序,来找出能使其中任何一个容器里的水恰好有 C 升,找出最少操作数并给出操作过程。
输入:
有且只有一行,包含 3 个数A,B,C(1≤A,B≤100,C≤max(A,B))。
输出:
最小操作数及其路径,如果不可能拿到目标容量输出“impossible”。
样例输入:
3 5 4
输出:
6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)
题目分析:这个题乍一看甚至像是个思维题,但仔细观察后,发现了状态选择+最少步骤+路径输出,这不是搜索的标准公式吗(doge),所以自然对每种状态进行同步选择,再依次扩散开,明显是个BFS。
于是我感觉轻松了。
可是让人头疼的地方来了,BFS找终点和步骤数那倒是没问题,可是怎么输出路径呢,每一步都可能不止一种选择合法啊,不到最后不知道那条路才是通往终点的啊。
这时有一种思路:每走出一步,记录下这一步的父亲,也就是我是从哪里走进来的,根据这样一直记录到终点,最后从终点回溯,一直寻找父亲就行了。
其实这题有个弱化版:也是 BFS+递归回溯输出路径,可以先写写。
详情见主页POJ-3984
那么话不多说,这里直接贴代码了
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long llint;
const int N=1e3;
struct Node{
int v_a,v_b;
int temp;
int cnt;
}pre[N][N];
//结构体定义A、B杯子里水的量、temp表示进行哪种操作、cnt代表这是第几步
//pre数组比较重要了:pre[i][j]代表A杯有i升,B杯有j升时,上一个状态是谁。(父亲)
bool vis[N][N];//对状态的标记,重复的状态不需要入队
int A,B,C;
void My_out(Node end){//递归函数输出路径
if(end.temp==-1){//由于我将起点的操作类型命为-1,所以找到-1,代表回到起点了,开始回溯输出
return ;
}
My_out(pre[end.v_a][end.v_b]);
switch(end.temp){//根据回溯回来的对应操作类型输出路径
case 1:cout << "FILL(1)" << endl;break;
case 2:cout << "FILL(2)" << endl;break;
case 3:cout << "DROP(1)" << endl;break;
case 4:cout << "DROP(2)" << endl;break;
case 5:cout << "POUR(1,2)" << endl;break;
case 6:cout << "POUR(2,1)" << endl;break;
}
}
void BFS(){//广搜
Node st;
st.v_a=0;
st.v_b=0;
st.cnt=0;
st.temp=-1;
//定义起点
queue<Node>q;
q.push(st);
while(q.size()){
Node u,v;
u=q.front();
q.pop();
if(u.v_a==C||u.v_b==C){//找到终点开始输出,结束搜索
cout << u.cnt << endl;
My_out(u);
return;
}
v.cnt=u.cnt+1;//在u状态的下一步有很多种可能,但是每一种可能的步数都是u+1
if(u.v_a!=A){//灌满A
v.v_a=A;
v.v_b=u.v_b;
if(!vis[v.v_a][v.v_b]){//需要此状态未被入队(下同)
vis[v.v_a][v.v_b]=1;
v.temp=1;
pre[v.v_a][v.v_b]=u;
q.push(v);//记录操作、父亲并且入队(下同)
}
}
if(u.v_b!=B){//灌满B
v.v_a=u.v_a;
v.v_b=B;
if(!vis[v.v_a][v.v_b]){
vis[v.v_a][v.v_b]=1;
v.temp=2;
pre[v.v_a][v.v_b]=u;
q.push(v);
}
}
if(u.v_a){//抽空A
v.v_a=0;
v.v_b=u.v_b;
if(!vis[v.v_a][v.v_b]){
vis[v.v_a][v.v_b]=1;
v.temp=3;
pre[v.v_a][v.v_b]=u;
q.push(v);
}
}
if(u.v_b){//抽空B
v.v_a=u.v_a;
v.v_b=0;
if(!vis[v.v_a][v.v_b]){
vis[v.v_a][v.v_b]=1;
v.temp=4;
pre[v.v_a][v.v_b]=u;
q.push(v);
}
}
if(u.v_a&&u.v_b!=B){//将A的水倒进B(有两种情况:倒光和倒不光,判断一下就好)(下同)
if(u.v_a+u.v_b>B){
v.v_a=u.v_a-(B-u.v_b);
v.v_b=B;
}
else{
v.v_a=0;
v.v_b=u.v_a+u.v_b;
}
if(!vis[v.v_a][v.v_b]){
vis[v.v_a][v.v_b]=1;
v.temp=5;
pre[v.v_a][v.v_b]=u;
q.push(v);
}
}
if(u.v_b&&u.v_a!=A){//将B的水倒进A
if(u.v_a+u.v_b>A){
v.v_a=A;
v.v_b=u.v_b-(A-u.v_a);
}
else{
v.v_a=u.v_a+u.v_b;
v.v_b=0;
}
if(!vis[v.v_a][v.v_b]){
vis[v.v_a][v.v_b]=1;
v.temp=6;
pre[v.v_a][v.v_b]=u;
q.push(v);
}
}
}
cout << "impossible" << endl;//整个BFS结束了,该有的状态都检索完了依然没有找到,就不可能了
}
int main(){
cin >> A >> B >> C;
BFS();
return 0;
}