UVA - 10603 Fill(BFS求最小值问题)

题目:

给出三个杯子(没有刻度线)的容量,起初之后第三个杯子是满的,其他的两个杯子是空的,容量分别是a、b、c。问最少需要倒多少升水才能让

某一个杯子中的水有d升?如果不能恰好做到d升,就让某一个杯子里的水是D升,其中D<d并且尽量接近d。(1≤a,b,c,d≤200)。要求输出最少的

倒水量和目标水量d或D。

思路:

菜是原罪,需要赎罪啊!!

1.一看到这种求最小值的问题很应该想到是用BFS了。

2.BFS需要标记状态呀,那201*201*201=8120601这内存有些吃不消啊,那前两个杯子的水固定了,总的水量又是不变的,那只用前两个杯子的

水量就可以标记所有可能的状态了。这样就把状态缩小到201*201了。

3.Node结构体中保存的是一个状态,“状态”这个词很玄乎啊,有到当前状态已经倒过的水量dist和三个杯子中还有的水量v[0]、v[1]、v[2]。

4.紫书的代码写的优美啊!!值的好好的学习!!

扫描二维码关注公众号,回复: 5039385 查看本文章

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define MAX 1000000009
#define FRE() freopen("in.txt","r",stdin)
#define FRO() freopen("out.txt","w",stdout)
using namespace std;
typedef long long ll;
const int maxn = 205;
struct Node{
    int v[3],dist;//dist保存的是到当前状态已经倒过的最小水量
    bool operator<(const Node& rhs)const{
        return dist>rhs.dist;
    }
};
int vis[maxn][maxn],cap[3],ans[maxn];

void update_ans(const Node& u){//更新答案
    for(int i=0; i<3; i++){
        int d=u.v[i];
        if(ans[d]<0 || u.dist<ans[d])
            ans[d] = u.dist;
    }
}

void solve(int a,int b,int c,int d){
    cap[0]=a;cap[1]=b;cap[2]=c;//记录目标状态的水量是多少
    memset(vis,0,sizeof(vis));//清空所有的可能出现的状态
    memset(ans,-1,sizeof(ans));//清空答案数组
    priority_queue<Node>q;

    Node start;//初始状态,按照题目要求赋值
    start.dist = 0;
    start.v[0] = start.v[1] = 0;
    start.v[2] = c;
    q.push(start);
    vis[0][0] = 1;//标记a、b杯子为空的状态
    while(!q.empty()){
        Node u = q.top();q.pop();
        update_ans(u);
        if(ans[d]>=0) break;//如果已经找到的答案就break
        for(int i=0; i<3; i++){//i表示的是倒出水的杯子
            for(int j=0; j<3; j++){//j表示的是被倒出水的杯子
                if(i!=j){//自己不能给自己倒水
                    if(u.v[i]==0 || u.v[j]==cap[j])continue;//如果倒出水的杯子里没有水
                                                            //被倒的杯子已经满了,就进行下一个状态
                    int amount = min(cap[j], u.v[i]+u.v[j])-u.v[j];//重点了!!
                    /*
                        要倒一定是全部倒出来,所以j杯子被倒水之后有两种状态
                        1.倒之后满了
                        2.倒之后没有满
                        从这两种状态中取一个最小的水量记录下来
                    */
                    Node u2;//赋值一个新的状态
                    memcpy(&u2, &u, sizeof(u));
                    u2.dist = u.dist+amount;
                    u2.v[i] -= amount;
                    u2.v[j] += amount;
                    if(!vis[u2.v[0]][u2.v[1]]){//如果这个状态没有出现过,就标记一下入队列
                        vis[u2.v[0]][u2.v[1]] = 1;
                        q.push(u2);
                    }
                }
            }
        }
    }
    while(d>=0){//遍历答案并输出
        if(ans[d] >= 0){
            printf("%d %d\n",ans[d],d);
            return;
        }
        d--;
    }
}

int main(){
    int T,a,b,c,d;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        solve(a,b,c,d);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sykline/p/10311292.html