week2 B - Pour Water

问题描述:

倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空,同理,“fill B” 表示倒满B杯,"empty B"表示倒空B杯,“pour B A” 表示把B的水倒到A杯并且把A杯倒满或B倒空。现有两个容量为A,B的杯子,无限水,如果要得到C容积的水,应该怎样操作?

Input:
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。

Output:
你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。

解题思路:

深究原理,此题与迷宫路径相似,将状态用坐标表示,从起始状态到目的状态,中间经过一系列不同的状态,而由一个状态变为另一种状态,只有问题描述中的六种方法,故思考得,要记录每一状态的前一状态以及由何种方法转变。采用深度优先搜索(BFS)方法把从初始状态能够到达的不同状态入队列,直到达到目标状态或队列为空。

主要的数据结构:
① 方法标记二维数组sign——表示当前状态由前一状态经何种转变而来。0表示该状态未到达,1表示该点为初始状态,2、3、4、5、6、7分别对应各函数,即该状态由该方法转变而来,8对应目标状态。
② 标记当前状态的前一状态的pair<int,int>二维数组r——用坐标表示状态,记录该状态的前一状态。

步骤:(类似树的按层遍历)

  1. 访问初始点(sx,sy),并将其标记为已访问过,sign置为1。
  2. 访问(sx,sy)的所有未被访问过可到达的状态点,并均标记为已访问过,即sign置为相应方法,前一坐标置为(sx,sy),将这些点加入到队列中。
  3. 再按照队列中的次序,访问每一个顶点(sx,sy)的所有未被访问过可到达的状态点,并做上相应标记,加入到队列中,依此类推。
  4. 直到到达终点(sign为8)或者初始状态能到达的所有状态都被访问了为止。

输出方法模块:
从初始点到目标点,每一个状态都记录了前一个状态以及相应的方法sign,故从目的状态往前遍历,借用栈结构保存sign,while循环,只要该点的sign不为1(没有到达初始状态),当前点的sign入栈,移向前一位置。最后,从栈中依次弹出sign值,输出对应方法即可。

实验代码:

#include<iostream>
#include<queue>
#include<utility>
#include<stack>
using namespace std;
int sign[1001][1001]={0};		

int A,B,C; 		//全局变量 
typedef pair<int,int> pairtype;
pairtype r[1001][1001];		//存储前一位置 
void fillA(int& a,int& b)
{
	a=A;
//	return make_pair(a,b);
}
void fillB(int& a,int& b)
{
	b=B;
}
void emptyA(int& a,int& b)
{
	a=0;
}
void emptyB(int& a,int& b)
{
	b=0;
}
void  AtoB(int& a,int& b)		//将A倒空或者将B倒满,总之不溢出 
{
	if(a<=B-b)
	{
		b+=a;
		a=0;		//A倒空 ,!!一定要注意顺序 
	}
	else 
	{
		a=a-(B-b);		 
		b=B;		//B倒满 
	}
}
void BtoA(int& a,int& b)		//A倒满或者B倒空 
{
	if(b<=A-a)	
	{
		a+=b;		
		b=0;		//B倒空 
	}
	else
	{
		b=b-(A-a);	
		a=A;		//A倒满 
	} 
} 
pairtype pourtoC(int sa,int sb)		//初始状态,返回末状态 
{
	queue<pairtype> q;
	pairtype p1(sa,sb);
	sign[sa][sb]=1;		//!标记!表示该节点已经访问过 
	if(sa==C||sb==C)
		return p1; 
	q.push(p1);		//初始状态队列
	while(!q.empty()) 		//调用六种函数 
	{
		p1=q.front();
		q.pop();
		int x1=p1.first,x2=p1.first,x3=p1.first,x4=p1.first,x5=p1.first,x6=p1.first;		//现有状态 
		int y1=p1.second,y2=p1.second,y3=p1.second,y4=p1.second,y5=p1.second,y6=p1.second;
		
		//该点可能到的几种状态 
		fillA(x1,y1);
		if(!sign[x1][y1])		//该点未到达
		{
			r[x1][y1]=p1;
			q.push(make_pair(x1,y1));
			sign[x1][y1]=2;		//表示由fillA方法而来	
		} 
		else
		{
			if(sign[x1][y1]==8)
			{
				r[x1][y1]=p1;
				sign[x1][y1]=2;
				return make_pair(x1,y1);
			}
		}
		
		emptyA(x2,y2);
		 if(!sign[x2][y2])		//该点未到达
		{
			r[x2][y2]=p1;
			q.push(make_pair(x2,y2));
			sign[x2][y2]=3;		//表示由fillA方法而来	
		} 
		else
		{
			if(sign[x2][y2]==8)		//到达终点 
			{
				r[x2][y2]=p1;
				sign[x2][y2]=3;
				return make_pair(x2,y2);
			}
		}
		
		AtoB(x3,y3);
		 if(!sign[x3][y3])		//该点未到达
		{
			r[x3][y3]=p1;
			q.push(make_pair(x3,y3));
			sign[x3][y3]=4;		//表示由fillA方法而来	
		} 
		else
		{
			if(sign[x3][y3]==8)		//到达终点 
			{
				r[x3][y3]=p1;
				sign[x3][y3]=4;
				return make_pair(x3,y3);
			}	
		}
		
		fillB(x4,y4);
		if(!sign[x4][y4])		//该点未到达
		{
			r[x4][y4]=p1;
			q.push(make_pair(x4,y4));
			sign[x4][y4]=5;		//表示由fillA方法而来	
		} 
		else
		{
			if(sign[x4][y4]==8)
			{
				r[x4][y4]=p1;
				sign[x4][y4]=5;
				return make_pair(x4,y4);
			}
		}
		
		emptyA(x5,y5);
		 if(!sign[x5][y5])		//该点未到达
		{
			r[x5][y5]=p1;
			q.push(make_pair(x5,y5));
			sign[x5][y5]=6;		//表示由fillA方法而来	
		} 
		else
		{
			if(sign[x5][y5]==8)		//到达终点 
			{
				r[x5][y5]=p1;
				sign[x5][y5]=6;
				return make_pair(x5,y5);
			}
		}
		
		BtoA(x6,y6);
		 if(!sign[x6][y6])		//该点未到达
		{
			r[x6][y6]=p1;
			q.push(make_pair(x6,y6));
			sign[x6][y6]=7;		//表示由fillA方法而来	
		} 
		else
		{
			if(sign[x6][y6]==8)		//到达终点 
			{
				r[x6][y6]=p1;
				sign[x6][y6]=7;
				return make_pair(x6,y6);
			}
		}
	}
	return  make_pair(-1,-1); 
} 

void output(int tx,int ty)		//从(tx,ty)状态返回 
{
	stack<int> s;		//存储中间函数的栈,因为要倒着输出 
	int zt=sign[tx][ty];
	pairtype p(tx,ty);
	int x,y;
	while(zt!=1)		//没有回到初始点 
	{
		s.push(zt);
		x=p.first;
		y=p.second;
		p=r[x][y];		//找到前一位置 
		zt=sign[p.first][p.second];		//前一位置的状态 
	}
	while(!s.empty())
	{
		int zt=s.top();
		s.pop();
		switch(zt)
		{
			case 2:{
				cout<<"fill A"<<endl;
				break;
			}
			case 3:{
				cout<<"empty A"<<endl;
				break;
			}
			case 4:{
				cout<<"pour A B"<<endl;
				break;
			}
			case 5:{
				cout<<"fill B"<<endl;
				break;
			}
			case 6:{
				cout<<"empty B"<<endl;
				break;
			}
			case 7:{
				cout<<"pour B A"<<endl;
				break;
			}
		}
	}
	cout<<"success"<<endl;
}
int main(void)
{
	while(cin>>A>>B>>C)
	{
		//数组初始化 
		for(int i=0;i<=A;i++)
			for(int j=0;j<=B;j++)
			{
				if(i==C||j==C)
					sign[i][j]=8;
				else 
					sign[i][j]=0;
				r[i][j].first=-1;
				r[i][j].second=-1;
			}
		pairtype p=pourtoC(0,0);
		if(p.first!=-1)
			output(p.first,p.second);
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值