题目链接:https://vjudge.net/problem/UVA-10603
题意:设三个杯子的容量分别是a,b,c,最初只有第三个被子装满了c升水,其它两个杯子为空。最少需要倒多少升水才能让某一个杯子中的水有d升呢?如果无法做到恰好d升,就让某一个杯子里的水是d2升,其中d2<d,并且尽量接近d。(1<=a,b,c,d<=200).要求输出最少的倒水量和目标水量。
分析:本题的目标是倒的水量最少而不是步数最少。实际上,水量最少时步数不一定最少。例如: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,bfs搜索即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int INF=0x7fffffff;
const int MOD=1e9+7;
const int maxn=205;
int vis[maxn][maxn],cap[3],ans[maxn];
int a,b,c,d;
struct node{
int v[3],dist;
bool operator < (const node& rhs) const{
return dist>rhs.dist;
}
};
void init(){
cap[0]=a;cap[1]=b,cap[2]=c;
memset(vis,0,sizeof(vis));
memset(ans,-1,sizeof(ans));
}
void update_ans(node &u){
for(int i=0;i<3;i++){
int d=u.v[i];
if(ans[d]<0||u.dist<ans[d]) ans[d]=u.dist;
}
}
void solve(){
init();
priority_queue<node> q;
node start;
start.dist=0;start.v[0]=0;start.v[1]=0;start.v[2]=c;
q.push(start);
vis[0][0]=1;
while(!q.empty()){
node u=q.top();q.pop();
update_ans(u);
if(ans[d]>=0) break;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(i!=j){
if(!u.v[i]||u.v[j]==cap[j]) continue;
int amount=min(cap[j],u.v[i]+u.v[j])-u.v[j];
node u2;
memcpy(&u2,&u,sizeof(u));
u2.dist=u.dist+amount;
u2.v[i]-=amount;
u2.v[j]+=amount;
if(!vis[u2.v[0]][u2.v[1]]){
vis[u2.v[0]][u2.v[1]]=1;
q.push(u2);
}
}
}
}
}
while(d>=0){
if(ans[d]>=0){
cout<<ans[d]<<' '<<d<<endl;
return;
}
d--;
}
}
int main() {
//freopen("in.txt","r",stdin);
int T;
cin>>T;
while(T--){
cin>>a>>b>>c>>d;
solve();
}
return 0;
}