(不易)POJ-3414 BFS中的路径还原

题目大意:

给出了两个瓶子的容量A,B, 以及一个目标水量C,

对A、B可以有如下操作:

FILL(i)       装满i瓶

DROP(i)     清空i瓶

POUR(i,j)    把i瓶都倒入j瓶中,若j已满则超出的部分的仍留在i中

问经过哪几个操作后能使得任意一个瓶子的残余水量为C。


题目链接:点击打开链接


分析:这题很明显用BFS,BFS其实也就一个模版并没有难点,所以最少操作次数很容易求出,这里的难点在于按顺序输出所有的操作,这个问题在最短路问题中也是独立出来的一个问题—路径还原。我们可以把每一个操作都当成是一个走法,于是对操作的输出便转化称了路径的还原。如果还没有接触个这个问题的童鞋们可以先看看我这篇文章:点击打开链接

相信大家在看完路径还原的方法与技巧之后,对这题应该也是有一点思路了。不错,这个题我们会用到文章最后的2个常见问题的解决技巧。我用0-5来分别代表BFS的5个入口(或者说方向),并且在记录路径的同时记录下这些不同入口。我会在代码注释中更加详细地进行解释的,请看完代码。


附上代码:

#include<iostream>    //POJ-3414
#include<queue>
#include<algorithm>
using namespace std;
int d[105][105];
bool vis[105][105];
int a, b, c;
int A, B;
struct st           //x,y分别代表1,2容器中当前的水量
{
	int x, y;
	st(int a = 0, int b = 0){ x = a, y = b; }
};
struct Prv               //用来记录路径(注意这里并不是真正意义上的路!),prv[x1][y1].last_x=x2,prv[x1][y1].last_y=y2
{						 //代表了(x1,y1)的前驱节点为(x2,y2),prv[x1][y1].kase=i则代表(x2,y2)是经由动作i到达的(x1,y1)
	int last_x, last_y;
	int kase;
}prv[105][105];
queue<st> q;
st solve(st &v, int kase)
{
	int x = v.x, y = v.y;
	if (kase == 0) x = a;
	else if (kase == 1) y = b;
	else if (kase == 2) x = 0;
	else if (kase == 3) y = 0;
	else if (kase == 4)
	{
		int ty = y;
		y = min(b, x + y);
		x = x + ty - y;
	}
	else
	{
		int tx = x;
		x = min(a, x + y);
		y = y + tx - x;
	}
	return st(x, y);
}
int bfs()
{
	q.push(st(0, 0));
	vis[0][0] = 1;
	prv[0][0].kase = -1;
	while (!q.empty())
	{
		st v = q.front();
		q.pop();
		for (int i = 0; i<6; i++)      //0-5分别代表FILL(1),FILL(2),DROP(1),DROP(2),POUR(1,2),POUR(2,1)这6种操作(动作)
		{
			st t = solve(v, i);          //求出执行操作i之后的状态(即1,2容器中的当前水量)
			int fx = t.x, fy = t.y;
			if (!vis[fx][fy])
			{
				vis[fx][fy] = 1;
				d[fx][fy] = d[v.x][v.y] + 1;
				q.push(st(fx, fy));
				Prv &tt = prv[fx][fy];       //记录下路径,注意一定别忘了&!!!
				tt.last_x = v.x;
				tt.last_y = v.y;
				tt.kase = i;
				if (fx == c || fy == c)     //注意出口放在!vis[fx][fy]里,所以若一开始的(0,0)就满足条件那么结果肯定不对
				{							//但这题C!=0,所以可以放心,否则需在主函数中判断一下
					A = fx, B = fy;
					return d[fx][fy];
				}
			}
		}
	}
	return -1;
}
int main()
{
	scanf("%d%d%d", &a, &b, &c);
	int ans = bfs();            
	if (!~ans) printf("impossible\n");
	else
	{
		printf("%d\n", ans);
		Prv t = prv[A][B];
		vector<st> path;              //将路径放入path中
		path.push_back(st(A, B));
		for (; t.kase != -1; t = prv[t.last_x][t.last_y])
			path.push_back(st(t.last_x, t.last_y));
		reverse(path.begin(), path.end());       //由于路径是从目的地到起点,所以要反转
		for (int i = 1; i < path.size(); i++)     //输出路径,由于起始状态到其下一个状态的操作记录在后一个状态的prv里,所以i从1开始
		{
			int t = prv[path[i].x][path[i].y].kase;
			if (t == 0) printf("FILL(1)");
			else if (t == 1) printf("FILL(2)");
			else if (t == 2) printf("DROP(1)");
			else if (t == 3) printf("DROP(2)");
			else if (t == 4) printf("POUR(1,2)");
			else printf("POUR(2,1)");
			printf("\n");
		}
	}
	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值