Uva 倒水问题

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

const int maxn=200+5;
int cup[3]; //方便遍历
int mp[maxn][maxn],ans[maxn];//mpbfs判重,ans记录每个状态不同杯子的水量
struct node{
    int v[3],dis;
    bool operator < (const node&rhs) const{//从状态节点的dis最小处扩展,所以最短路问题也是从dis的最小处扩展
        return dis>rhs.dis;
    }
};
void rcu(node&u){
    for(int i=0;i<3;i++){
        if(ans[u.v[i]]<0||u.dis<ans[u.v[i]])//如果状态u的dis更小则更新
            ans[u.v[i]]=u.dis;
    }
}
void bfs(int a,int b,int c,int d){
    cup[0]=a,cup[1]=b,cup[2]=c;
    priority_queue<node> que;
    node start;
    start.v[0]=0,start.v[1]=0,start.v[2]=c,start.dis=0;
    que.push(start);
    memset(mp,0,sizeof(mp));
    memset(ans,-1,sizeof(ans));
    mp[0][0]=1;
    
    while(que.size()){//所需要找到的dis因为必须执行到que为空结束才能找到,中间无法记录需要的节点,所以用ans保存dis和每个可行的状态的水量情况
        node u=que.top();que.pop();
        rcu(u);
        if(ans[d]>=0)break;
        //扩展子节点,依照dis小方向扩展
        //从第i个杯子向第j个被子倒水
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                if(j!=i){
                    if(!u.v[i]||u.v[j]==cup[j])continue;
                    int amount=min(cup[j],u.v[i]+u.v[j])-u.v[j];//向j杯子倒amount的水
                    node u1;
                    memcpy(&u1,&u,sizeof(u));//扩展子节点,u为根节点
                    u1.dis=u.dis+amount;
                    u1.v[i]-=amount;
                    u1.v[j]+=amount;
                    if(!mp[u1.v[0]][u1.v[1]]){
                        que.push(u1);
                        mp[u1.v[0]][u1.v[1]]=1;
                    }
                }
    }
//    for(int i=0;i<10;i++)printf("%d",ans[i]);
//    cout<<endl;
    while(d>=0){
        if(ans[d]>=0){
            printf("%d %d\n",ans[d],d);
            return;
        }
        d--;
    }
}
int main(){
    int t,a,b,c,d;
    cin>>t;
    while(t--){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        bfs(a,b,c,d);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值