Fill UVA - 10603 (bfs最短路径)

https://cn.vjudge.net/problem/UVA-10603

题目大意:给出三个杯子的容量,开始时只有第三个杯子装满了水。给出目标容量d,求出最后要来回倒出多少升水可以使某个杯子的容量达到d。

分析:

假设在某个状态:第一个杯子有v0升水,第二个杯子有v1升水,第三个杯子有v2升水,则记该状态为(v0,v1,v2)。

把状态想象为结点,倒水的过程为线,则题目便转化为了图的问题,我们需要求初始状态到目标状态的最短距离。

但本题的目标是倒水量最少,不是步数最少,步数最少不一定倒水量最少!!

所以我们要改进一下算法,因为普通的bfs入队和出队对应的为步数,所以我们需要优先队列存储结点,在结点中重载运算符来定义排序。

还有一点要注意:题目有三个杯子不论怎么倒水都无法到达d升的情况,那么就让容量达到d'升,其中d<d',并且d'无限接近d。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 210;
struct Node{
	int water[3];
	int dist;
	bool operator < (const Node& node) const {
		return dist>node.dist;
	}
};
int vis[maxn][maxn], ans[maxn], full[3];

void update_ans(const Node cur) {
	for(int i = 0; i < 3; i++) {
		int w = cur.water[i];
		if(ans[w]<0 || ans[w]>cur.dist)//不要忘记无法达到d升的情况
			ans[w] = cur.dist;
	}
}

void bfs(int a, int b, int c, int d) {
	priority_queue<Node> q;
	full[0] = a, full[1] = b, full[2] = c; 
	Node start;
	start.water[0] = 0;
	start.water[1] = 0;
	start.water[2] = c;
	start.dist = 0;
	vis[start.water[0]][start.water[1]] = 1;
	q.push(start);
	while(!q.empty()) {
		Node cur = q.top();q.pop();
		update_ans(cur);
		if(ans[d] >= 0) break;
		for(int i = 0; i < 3; i++) 
			for(int j = 0; j < 3; j++) if(i!=j) {
			if(cur.water[i]==0 || cur.water[j]==full[j])
				continue;
			//默认为i给j倒水,求倒出水的容量
			int add = min(full[j], cur.water[i]+cur.water[j])-cur.water[j];
			Node nt;
			memcpy(&nt, &cur, sizeof(cur));
			nt.dist = cur.dist + add;
			nt.water[i] -= add;
			nt.water[j] += add;
			if(!vis[nt.water[0]][nt.water[1]]) {
				q.push(nt);
				vis[nt.water[0]][nt.water[1]] = 1;
			}
		}
	}
	while(d>=0) {//解决无法达到d升的情况
		if(ans[d]>=0) {
			cout << ans[d] << " " << d << endl;
			return;
		}
		d--;
	}
}
 
int main()
{
	freopen("i.txt","r",stdin);
    freopen("o.txt","w",stdout);
    int n,a,b,c,d;
    cin >> n;
    while(n--) {
    	memset(vis, 0, sizeof(vis));
    	memset(ans, -1, sizeof(ans));
    	cin >> a >> b >> c >> d;
    	bfs(a,b,c,d);
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值