倒水问题(Fill,UVA 10603) lrj白书 p202

看着lrj的代码自己敲了一遍,还没调试成功。。。。

有时间再进行完善

 /*
 状态start到各个状态u1,u2,u3.....
 的倒水量分别为u1.dist,u2.dist,u3.dist....
 */
 /*
 按倒水量递增的顺序去寻找当前状态的v[0],v[1],v[2]是否为d
 但是程序实现的时候我们寻找目标水量是根据 ans[d]>0,而不是对每一个状态的v[]进行遍历
 为什么这样做呢?当然是因为麻烦,ans[d]>0很直接地说明了v[]有三者之一为d
 */
 #include <cstdio>
 #include <cstring>
 #include <queue>
 using namespace std;

 struct Node{
	 int v[3],dist;
	 bool operator <(const Node &u) const{
		 return dist >u.dist;
   }
 };


 const int maxn=200+5;
 int vis[maxn][maxn],cap[3],ans[maxn];//因为只有三只杯子,所以记录前两只杯子的状态即可
 void update_ans(const 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;//每次有新的状态时,记录形成d升水所需要的最小倒水量
	 }
 }
 void solve(int a,int b,int c,int d){
	 cap[0]=a;
	 cap[1]=b;
	 cap[2]=c;
	 memset(vis,0,sizeof(vis));//vis[]记录三元组状态是否重复
	 memset(ans,-1,sizeof(ans));//ans[]记录当前的最小倒水量
	 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;//设置初始状态为已访问
	 /*!当所有状态都被访问完,就算ans[d]<0,while{}也停止
	 !!当ans[d]>0,表示输出所求的状态已找到,停止循环*/
	 while (!q.empty()){
		 Node u=q.top();q.pop();//对优先队列表头(即当前队列结点.dist最小)的结点进行访问
		 update_ans(u);
		 if (ans[d]>=0)  break;
		 //从v[i]--v[j]加水,加多少水呢?只要能加,就加到不能加为止
		 for(int i=0;i<3;i++)
			 for(int j=0;j<3;j++)if(i!=j){
				 if(u.v[i]==0||u.v[j]==cap[j]) continue; //过渡到下一个i或下一个j
				 int amount=min(cap[j],u.v[i])-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){
			 printf("%d %d\n",ans[d],d);
			 return;
		 }
		 d--;
	 }
 }

 int main(){
	 int T,a,b,c,d;
	 scanf("d%",&T);
	 while(T--){
		 scanf("%d%d%d%d",&a,&b,&c,&d);
		 solve(a,b,c,d);
	 }
	 return 0;
 }

  

posted @ 2017-02-07 18:20  执蘖执蘖  阅读(158)  评论(0编辑  收藏  举报