题目大意:
给你三个瓶子,并且给你他们的体积分别是a,b,c,但是一开始第一个和第二个杯子是空的,只有第三个是满的,然后给你一个d,问你通过许多次倒水使得其中一个水杯的体积为d,你倒的水的最小值是多少?
思路:
他求的是倒水的最小值,并不是倒水的最小次数,先把这个分清楚。
先把三个目前状态下的状态存储起来,然后每次选倒水量最小的(A*思路)损失函数G=水的量+到当前状态的水量。
用优先队列存储,可以实现每次取最小水量的元素,用一个一维数组ans[i],表示某个水杯的容量i的步数,一开始设置为-1.
具体思路在代码里面,应该写的已经很清楚了。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e3+10;
int ans[maxn];
int cap[3],vis[maxn][maxn];

struct node{
	int dist;//步数 
	int v[3];//三个杯子目前存储的水 
	node(int a,int b,int c,int d){
		dist=a;v[0]=b;v[1]=c;v[2]=d;
	}
	node(){}
	bool operator<(const node &a)const {
		return dist>a.dist;
	}
};

void uadate(node a){
	for(int i=0;i<3;i++){
		int t=a.v[i];
		if(ans[t]==-1||(ans[t]!=-1&&ans[t]>a.dist)){//修改每个容量的步数
			ans[t]=a.dist;
		}
	} 
}
void slove(int a,int b,int c,int d){
	priority_queue<node>st;
	memset(vis,0,sizeof(vis));
	memset(ans,-1,sizeof(ans));
	node s(0,0,0,c);
	cap[0]=a;cap[1]=b;cap[2]=c;
	st.push(s);
	while(!st.empty()){
		node now=st.top();
		st.pop();
		uadate(now);
		if(ans[d]>=0)break;//已经找到容量为d的杯子了
		for(int i=0;i<3;i++){//倒水的杯子 
			for(int j=0;j<3;j++){//被倒水的杯子 
				if(i!=j){//自己不能给自己倒水 
					if(now.v[i]==0||now.v[j]==cap[j])continue;//倒水的杯子没水,被倒水的杯子已经满了
						int water=min(cap[j],now.v[i]+now.v[j])-now.v[j];//求到了多少水 
						node tem;
						memcpy(&tem,&now,sizeof(now));
						tem.dist=now.dist+water; 
						tem.v[j]+=water;
						tem.v[i]-=water;
						if(!vis[tem.v[0]][tem.v[1]]){
							vis[tem.v[0]][tem.v[1]]=1;
							st.push(tem) ;
						}
				}
			}
		}
	}
	while(d>=0){
		if(ans[d]>=0){
			cout<<ans[d]<<" "<<d<<endl;
			break;
		}
		d--;
	}
}
int main(){
	int T,a,b,c,d;
	scanf("%d",&T);
		while(T--){
			scanf("%d%d%d%d",&a,&b,&c,&d);
			slove(a,b,c,d);
		}
	
}