POJ-3414:Pots(BFS+特殊标记+递归回溯输出路径)

题目:

给你两个容器,分别能装下 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;
} 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值