ZOJ Problem Set - 1005

【【搜索题】】

 

【题目】

 

Jugs


Time Limit: 1 Second     Memory Limit:32768 KB     Special Judge


In the movie "Die Hard 3", Bruce Willis and Samuel L. Jackson were confronted with the following puzzle. They were given a 3-gallon jug and a 5-gallon jug and were asked to fill the 5-gallon jug with exactly 4 gallons. This problem generalizes that puzzle.

You have two jugs, A and B, and an infinite supply of water. There are three types of actions that you can use: (1) you can fill a jug, (2) you can empty a jug, and (3) you can pour from one jug to the other. Pouring from one jug to the other stops when the first jug is empty or the second jug is full, whichever comes first. For example, if A has 5 gallons and B has 6 gallons and a capacity of 8, then pouring from A to B leaves B full and 3 gallons in A.

A problem is given by a triple (Ca,Cb,N), where Ca and Cb are the capacities of the jugs A and B, respectively, and N is the goal. A solution is a sequence of steps that leaves exactly N gallons in jug B. The possible steps are

fill A
fill B
empty A
empty B
pour A B
pour B A
success

where "pour A B" means "pour the contents of jug A into jug B", and "success" means that the goal has been accomplished.

You may assume that the input you are given does have a solution.

Input

Input to your program consists of a series of input lines each defining one puzzle. Input for each puzzle is a single line of three positive integers: Ca, Cb, and N. Ca and Cb are the capacities of jugs A and B, and N is the goal. You can assume 0 < Ca <= Cb and N <= Cb <=1000 and that A and B are relatively prime to one another.

Output

Output from your program will consist of a series of instructions from the list of the potential output lines which will result in either of the jugs containing exactly N gallons of water. The last line of output for each puzzle should be the line "success". Output lines start in column 1 and there should be no empty lines nor any trailing spaces.

 

Sample Input
3 5 4
5 7 3

Sample Output
fill B
pour B A
empty A
pour B A
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
empty B
pour A B
success

Source: Zhejiang University Local Contest 2001

【题意说明】

      给A、B两个空水壶以及它们的容量,用以下3个动作的组合:(1)给一个水壶注满水;(2)把一个水壶中的水倒入另一个水壶直到倒入水壶满或倒出水壶空;(3)把一个水壶的水全数倒空,使得最后B水壶的水量为给定值N。输入A、B水壶容量以及定值N,输出任意一种动作组合。

 

【解答】

(一)分析:用广度优先搜索解决。广搜树的节点状态为A、B的水量,由初始状态(A,B皆空)开始,以后每一个状态节点都生成它的可行的后继状态节点(注满A、注满B、清空A、清空B、A倒入B、B倒入A),进行广度优先搜索,并保存每个搜索节点的前一个节点。当搜到的某个节点有B水量为N时,根据保存的前一个节点回溯输出这条路径。搜索期间用reach[x][y]表示A水量为x、B水量为y的节点是否被搜过。

(二)代码

//action:1,fill A,2,fill B,10,empty A,20,empty B,12,AB,21,BA
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
//搜索节点
typedef struct node
{
public:
	int a,b;//A、B水量
	int action;//当前节点执行的动作(1表示注满A,2表示注满B,10表示清空A,20表示清空B,12表示A倒入B,21表示B倒入A)
	struct node *prior;//保存其父节点
}Node;
bool reach[1005][1005];//某节点状态是否搜索过
Node sd[1000*1000];
int main()
{
	int ca,cb,n,c;
	Node d,v;
	queue<Node> q;
	stack<Node> st;
	int i,j,num;
	while(cin>>ca>>cb>>n){
		for(i=0;i<1005;i++)
			for(j=0;j<1005;j++)
				reach[i][j]=false;
			while(!q.empty())
				q.pop();
			//初始化根节点的2个子节点(注满A\注满B)
			d.a=ca;
			d.b=0;
			d.action=1;
			d.prior=NULL;
			q.push(d);
			d.a=0;
			d.b=cb;
			d.action=2;
			d.prior=NULL;
			q.push(d);
			num=0;
			//开始搜索
			while(!q.empty()){
				v=q.front();
				q.pop();
				if(v.b==n)//找到解决方案
					break;
				if(reach[v.a][v.b])
					continue;
				reach[v.a][v.b]=true;
				sd[num]=v;
                if(v.a==0&&v.b!=0){
					if(v.b!=cb){//注满A
						d.a=ca;
						d.b=v.b;
						d.action=1;
						d.prior=&sd[num];
						q.push(d);
					}
                   if(v.b>ca){//B倒入A,A满
					   d.a=ca;
					   d.b=v.b-ca;
					   d.action=21;
					   d.prior=&sd[num];
					   q.push(d);
				   }
				   else if(v.b<ca){//B倒入A,B空
					   d.a=v.b;
					   d.b=0;
					   d.action=21;
					   d.prior=&sd[num];
					   q.push(d);
				   }
				}
                else if(v.b==0&&v.a!=0){
					if(v.a!=ca){//注满B
						d.a=v.a;
						d.b=cb;
						d.action=2;
						d.prior=&sd[num];
						q.push(d);
					}
					//A倒入B,A空
					d.a=0;
					d.b=v.a;
					d.action=12;
					d.prior=&sd[num];
					q.push(d);
				}
				else if(v.a!=0&&v.b!=0){
					if(v.a==ca&&v.b!=cb){
					//清空A
                      d.a=0;
					  d.b=v.b;
					  d.action=10;
					  d.prior=&sd[num];
					  q.push(d);
					  if(cb-v.b>ca)//A倒入B,A空
					  {
						  d.a=0;
						  d.b=v.b+ca;
						  d.action=12;
						  d.prior=&sd[num];
						  q.push(d);
					  }
                      else if(cb-v.b<ca)//A倒入B,B满
					  {
						  d.a=ca-(cb-v.b);
						  d.b=cb;
						  d.action=12;
						  d.prior=&sd[num];
						  q.push(d);
					  }
					  
					}
					else if(v.a!=ca&&v.b==cb){
						//清空B
						d.a=v.a;
						d.b=0;
						d.action=20;
						d.prior=&sd[num];
						q.push(d);
						//B倒入A,A满
						d.a=ca;
						d.b=cb-(ca-v.a);
						d.action=21;
						d.prior=&sd[num];
						q.push(d);
					}
					else if(v.a!=ca&&v.b!=cb){
                        d.a=ca;
						d.b=v.b;
						d.action=1;//注满A
						d.prior=&sd[num];
						q.push(d);
						d.a=v.a;
						d.b=cb;
						d.action=2;//注满B
						d.prior=&sd[num];
						q.push(d);
						d.a=0;
						d.b=v.b;
						d.action=10;//清空A
						d.prior=&sd[num];
						q.push(d);
						d.a=v.a;
						d.b=0;
						d.action=20;//清空B
						d.prior=&sd[num];
						q.push(d);
						if(cb-v.b>v.a){//A倒入B,A空
							d.a=0;
							d.b=v.b+v.a;
							d.action=12;
							d.prior=&sd[num];
							q.push(d);
						}
						else if(cb-v.b<v.a){//A倒入B,B满
							d.a=v.a-(cb-v.b);
							d.b=cb;
							d.action=12;
							d.prior=&sd[num];
							q.push(d);
						}
						if(ca-v.a>v.b){//B倒入A,B空
							d.a=v.a+v.b;
							d.b=0;
							d.action=21;
							d.prior=&sd[num];
							q.push(d);
						}
						else if(ca-v.a<v.b){//B倒入A,A满
							d.a=ca;
							d.b=v.b-(ca-v.a);
							d.action=21;
							d.prior=&sd[num];
							q.push(d);
						}
					}
				}
				num++;
			}
			d=v;
			st.push(d);
			while(d.prior!=NULL){//将路径回溯,节点保存到栈中
				d=*(d.prior);
				st.push(d);
			}
			while(!st.empty()){//栈中节点从头到尾输出
				d=st.top();
				st.pop();
				c=d.action;
				if(c==1)
					cout<<"fill A"<<endl;
				else if(c==2)
					cout<<"fill B"<<endl;
				else if(c==10)
					cout<<"empty A"<<endl;
				else if(c==20)
					cout<<"empty B"<<endl;
				else if(c==12)
					cout<<"pour A B"<<endl;
				else if(c==21)
					cout<<"pour B A"<<endl;
				
			}
			cout<<"success"<<endl;
	}
        return 0;
}
//Accepted

 


(解于2009/10)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值