隐式图--UVA - 10603 Fill

分析:假设在某个时刻,第1个杯子中有v0升水,第2个杯子中有v1升水,第3个杯子中有v2升水,称当时的系统状态为(v0,v1,v2);把‘状态’看成图中的结点,可以得到状态转移图;由于无论如何倒,杯子中的水量都是整数,因此第3个杯子的水量最多只有0,1,2,3 ... ,c共c+1种可能,同理,第2个杯子的水量共有b+1种可能;而一旦两个杯子的水量一样了,则第三个杯子水量也一样,所以最多的状态不会超过 201^2=40401;所以该问题实际上就是bfs求图的最短路,只不过这里不是真的路径最短,而是水量最少。


1、初始化优先队列,把最初状态入队列

2、取出水量最小的结点,更新水量表

3、如果水量已经达到d则退出

4、循环遍历6种状态,计算当前状态移动的水量并生成新的结点

5、若新结点未访问则入队列,继续第4步

6、回到第2步

7、从d开始查水量表,如果水量非负则输出结果


#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 200 + 5;
struct Node {
	int v[3], cnt;
	bool operator< (const Node& b) const { return cnt > b.cnt; }
};
int vis[maxn][maxn], ans[maxn], cap[3], d;

void update_ans(const Node& a)
{
	for(int i=0; i<3; i++) {
		int d = a.v[i];
		if(ans[d] < 0 || a.cnt < ans[d]) ans[d] = a.cnt;
	}
}

void solve()
{
    memset(vis, 0, sizeof(vis));
    memset(ans, -1, sizeof(ans));
    priority_queue<Node> q;
    Node st;
    st.cnt = 0; st.v[0] = 0; st.v[1] = 0; st.v[2] = cap[2];
    q.push(st);
    vis[0][0] = 1;
    while(!q.empty()) {
		st = q.top(); q.pop();
        update_ans(st);
        if(ans[d] >= 0) break;
        for(int i=0; i<3; i++) {
			for(int j=0; j<3; j++) if(i!=j){
				if(st.v[i]==0 || st.v[j]==cap[j]) continue;
                int amount = min(cap[j], st.v[i]+st.v[j]) - st.v[j];
                Node u;
                memcpy(&u, &st, sizeof(st));
                u.cnt = st.cnt + amount;
                u.v[i] -= amount;
                u.v[j] += amount;
                if(!vis[u.v[0]][u.v[1]]) {
					vis[u.v[0]][u.v[1]] = 1;
					q.push(u);
                }
			}
        }
    }

    while(d>=0) {
		if(ans[d]>=0) {
			printf("%d %d\n", ans[d], d);
			return ;
		}
		d--;
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--) {
		scanf("%d%d%d%d", &cap[0], &cap[1], &cap[2], &d);
		solve();
	}
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值