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;}
- UVA 10603 Fill(状态空间搜索,倒水问题)
- UVA 10603 Fill倒水问题
- 倒水问题(Fill,UVa 10603)
- 倒水问题(Fill,UVa 10603)
- UVa 10603 - Fill,经典倒水问题+隐式图搜索+dfs
- uva 10603 Fill(倒水问题 BFS)
- Fill (Uva 10603 bfs 倒水问题)
- UVA 10603 Fill(倒水问题)
- 例题7-8 UVA 10603 Fill 倒水问题
- UVA-10603 Fill 倒水问题 BFS+优先队列
- 例题7-8 倒水问题(Fill, UVa 10603)
- [隐式图搜索]Fill(倒水问题) UVA10603
- uva 10603(Fill, 隐式图搜索问题)
- uva 10603 Fill 搜索
- 10603 - Fill,倒水问题 bfs解
- uva 10603 倒水问题
- [中等] UVa OJ 10603 Fill BFS状态搜索
- uva 10603倒水问题(搜索 隐式图的最短路 )
- 实用插件(一)日历插件——My97DatePicker
- Network Security Configuration
- 腾讯OMG前端
- 柔性数组
- Github | 程序员七大生产力工具
- UVA 10603 Fill(状态空间搜索,倒水问题)
- Linux服务器风险检测与安全加固
- 剑指Offer_面试题21_包含min函数的栈
- 【2017多校 #Round 4 T7】【HDU6073】Matching In Multiplication
- Window 同时安装Python2和Python3 终极版
- kettle调用java代码处理数据
- 使用C++访问OPC Server的简单方法
- Java并发编程:线程池的使用
- unix编程哲学