【題目鏈接】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;
}