UVA 10603 Fill(状态空间搜索,倒水问题)

来源:互联网 发布:淘宝男士摩登舞服装 编辑:程序博客网 时间:2024/06/10 22:54

【题目链接】uva-10603

【题意】设3个杯子的容量分别为a, b, c,最初只有第3个杯子装 满了c升水,其他两个杯子为空。最少需要倒多少升水才能让某一个杯子中的水有d升呢?如果无法做到恰好d升,就让某一个杯子里的水是d'升,其中d'<d并且尽量接近d。 (1≤a,b,c,d≤200)。要求输出最少的倒水量和目标水量(d或者d')。

【样例】

Sample Input
2
2 3 4 2
96 97 199 62
Sample Output
2 2
9859 62

【分析】

      (刘汝佳说的够清楚了…直接复制了书上的分析)

       假设在某一时刻,第1个杯子中有v0升水,第2个杯子中有v1升水,第3个杯子中有v2升水,称当时的系统状态为(v0,v1,v2)。

       这里再次提到了“状态”这个词,它是理解很多概念和算法的关键。简单地说,它就是“对系统当前状况的描述”。例如,在国际象棋中,当前游戏者和棋盘上的局面就是刻画游戏进程的状态。

       把“状态”想象成图中的结点,可以得到如图7-16所示的状态图(state graph)。

       由于无论如何倒,杯子中的水量都是整数(按照倒水次数归纳即可),因此第3个杯子的 水量最多只有0, 1, 2,…, c共c+1种可能;同理, 第2个杯子的水量一共只有b+1种可能,第1个杯子一共只有a+1种可能,因此理论上状态最多有 (a+1)(b+1)(c+1)=8120601种可能性,有点大。幸运的是,上面的估计是不精确的。由于水的总量x永远不变,如果有两个状态的前两个杯子的水量都相同,则第3个杯子的水量也相同。换句话说,最多可能的状态数不会超过2012=40401。

       注意:本题的目标是倒的水量最少,而不是步数最少。实际上,水量最少时步数不一定最少,例如a=1, b=12, c=15, d=7,倒水量最少的方案是C->A, A->B重复7次,最后C里有7 升水。一共14步,总水量也是14。还有一种方法是C->B,然后B->A, A->C重复4次,最后C里有7升水。一共只有10步,但总水量多达20。因此,需要改一下算法:不是每次取出步数最少的结点进行扩展,而是取出水量最少的 结点进行扩展。这样的程序只需要把队列queue换成优先队列priority_queue,其他部分的代码不变。下面的代码把状态(三元组)和dist合起来定义为了一个Node类型,是一种常见的写法。

【总结】关于这道题,最大的感想是自己的代码写的真是太挫了,各种强行写。写完过后看了看刘汝佳的代码……默默重写了一遍。他写的真好啊。学习…

【代码】

#include<cstdio>#include<iostream>#include<queue>#include<algorithm>#include<cstring>using namespace std;const int maxn=205;int a,b,c,d;struct node{    int v[3],vol;    bool operator< (const node& rhs) const{        return vol>rhs.vol;    }};int vis[maxn][maxn],ans[maxn],cap[3];void update_ans(node u){    for(int i=0;i<3;i++){        int d=u.v[i];        if(ans[d]<0)//||ans[d]>u.vol)            ans[d]=u.vol;    }    return ;}priority_queue<node> p;int bfs(){    while(!p.empty()){        node e=p.top();        p.pop();        vis[e.v[0]][e.v[1]]=true;        update_ans(e);        if(ans[d]>=0)            break;        for(int i=0;i<3;i++){//倒水的            for(int j=0;j<3;j++){//被倒入的                if(e.v[i]==0||e.v[j]==cap[j])                    continue;                int amt=min(cap[j],e.v[i]+e.v[j])-e.v[j];                node s=e;                s.vol+=amt;                s.v[i]-=amt;                s.v[j]+=amt;                if(!vis[s.v[0]][s.v[1]]){                    p.push(s);                }            }        }    }    while(d>=0){        if(ans[d]>=0){            printf("%d %d\n",ans[d],d);            return 0;        }        d--;    }    return 0;}int main(){    int T;    scanf("%d",&T);    while(T--){        scanf("%d%d%d%d",&a,&b,&c,&d);        memset(vis,false,sizeof(vis));        memset(ans,-1,sizeof(ans));        while(!p.empty())            p.pop();        node s;s.v[0]=0;s.v[1]=0;s.v[2]=c;s.vol=0;        cap[0]=a;cap[1]=b;cap[2]=c;        vis[0][0]=true;        p.push(s);        bfs();    }    return 0;}

原创粉丝点击