uva 10603(Fill, 隐式图搜索问题)

题目大意:

有三个杯子的容量分别为a, b, c,  最初只有第3个杯子装满了c升水,其他两个杯子为空。最少需要倒多少升水才能让某个水杯有d升,如果无法达到d升,找到某个杯子的水是d’升,其中d‘<d并且尽量接近d。(1< = a, b, c, d <= 200)。 要求输出最少的倒水量和目标水量(d或者d')。


题目分析:

首先,将三个杯子的水量看成是状态(v0, v1, v2); 那么我们可以描述出倒水过程中的状态变化:

这里我用结构体node 表示这个状态:

struct Node{
    int w[3], dist;  // w[i]表示第i个水杯中的水量; (i是从0开始计数) , dist表示目前为止最少的倒水量;
};

那么怎么表示状态的变化呢;

</pre><pre name="code" class="cpp">//尝试将第i杯子中的水倒入j杯子中;
Node v = u; // u为之前状态;
for i = 0 to 2
    for(j = 0 to 2)
        if(j == i) continue;  //不能自己给自己倒水;
            int water = min(v.w[i], cap[j]-v.w[j]);  //cap[i]表示i杯子的容积; water表示可以倒的水量; 
            v.w[i] -= water;
            v.w[j] += water;

有了前面的介绍我们回到问题中,怎么求最少的倒水量和目标水量呢?

我们从起始的状态开始,枚举所有可能变化的状态,进行保存记录,不就可以了;

每个杯子的水量最多有201种可能,那么所有的状态不会超过201*201 = 40401; 是可以进行遍历的;


代码如下:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

const int maxn = 200+5;
int cap[3], d;
struct Node{
    int w[3], dist;
};
int ans[maxn], vis[maxn][maxn];

void Init(){
    memset(vis, 0, sizeof(vis));
    memset(ans, -1, sizeof(ans));
}

void update_ans(Node &s){
    for(int i = 0; i < 3; ++i){
        int d = s.w[i];
        if(ans[d] < 0 || s.dist < ans[d]) ans[d] = s.dist;
    }
}

void bfs(){
    queue<Node> q;
    Node u, v;
    u.w[0] = u.w[1] = 0; u.w[2] = cap[2];
    u.dist = 0;
    vis[0][0] = 1;
    q.push(u);
    while(!q.empty()){
        u = q.front();
        q.pop();
        update_ans(u);
        for(int i = 0; i < 3; ++i)
            for(int j = 0; j < 3; ++j){
                if(i == j) continue;
                v = u;
                int water = min(u.w[i], cap[j]-u.w[j]);
                v.w[i] -= water;
                v.w[j] += water;
                v.dist += water;
                int ok = 0;
                for(int i = 0; i < 3; ++i){   // 一定注意这里, 不是访问过就不能访问了,如果倒水总量小于之前的,依旧要访问;
                    if(v.dist < ans[v.w[i]])
                        ok = 1;
                }
                if(!vis[v.w[0]][v.w[1]] || ok){
                    vis[v.w[0]][v.w[1]] = 1;
                    q.push(v);
                }
            }
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d%d%d", &cap[0], &cap[1], &cap[2], &d);
        Init();
        bfs();
        for(int i = d; i >= 0; --i)
            if(ans[i] >= 0){    //之前写成>找了很长时间的bug, 注意这些细节;
                printf("%d %d\n", ans[i], i);
                break;
            }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值