【题目链接】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;
}