HDU-1495 非常可乐解题报告(BFS+模拟)(真的乐死我了)


补上牺牲昨天一整天复习时间写出来的破题的题解。(乞求别挂科(´;ω;`))

题目描述

题目:https://vjudge.net/problem/HDU-1495
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

Input

三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。

Output

如果能平分的话请输出最少要倒的次数,否则输出"NO"。

Sample Input

7 4 3

4 1 3

0 0 0

Sample Output

NO

3

思路分析

首先是考点分析上的难点。看到这一题,第一感觉是懵逼:这啥玩意呀?完全抓不到考点。随后,我百度了一下(开始作弊),知道了这道题是一道bfs…这真正刷新了我对bfs的认知:原来bfs能这么用啊。
如果往bfs的方向思考:初始状态就是s中装满可乐,n、m两个杯子空着,容量有限制。而需要选择的分支就是从哪个杯子倒到另外的哪一个杯子。所以,这里有3个杯子,所以每次都有6个分支(当然,这些分支有些是可以剪掉的)。
因此,思路就是:从其实状态开始,分别搜索6个分支情况,(重复情况不再搜索)再分别对这6种情况再进行搜索。而每一种情况有四个属性:s、n、m现在所有的可乐量以及现在已经倒的次数steps。可以建立一个结构体存储他们。

typedef struct{
	int s;
	int a;
	int b;
	int steps;
}condition;//我这里写a、b其实不好。这里也给我后期写倒可乐造成了一定的困扰。建议换成n、m。

下面就是终止条件的难点。惯性思维告诉我们:需要让两个杯子里的可乐相等且都是总量的一半时才终止。但实际并不可能。例如样例中4 1 3,n杯子并不能装下2的可乐。因为题目只是要求平分,而没有要求需要倒到两个杯子里,所以可以这么想:只需要s和max{n,m}两个杯子里的可乐相等且都等于s的一半时就可以终止了。

另外还有一个比较容易发现的点:因为杯子都是整数,所以分出来的两份可乐份数都只能是整数。如果原来可乐的份数就是奇数呢?直接输出后continue掉就行了。这样可以避免很多麻烦。

最后,还有一个怎么模拟这个过程的难点。这个过程比较繁琐(我被这一部分恶心到了)。需要注意的就是:倒入一个杯子里,除了倒入s之外,都有两种可能:能装满被倒入杯和装不满被倒入杯。因此很多需要分类讨论,这个过程就变得相当繁琐了。
思路理清之后,剩下的就是体力活了。但是,倒饮料的时候,一定要谨慎、细心。

完整代码

#include <iostream>
#include <queue>
#include <string.h> 
using namespace std;
typedef struct{
	int s;
	int a;
	int b;
	int steps;
}condition;

int main()
{
	int s,n,m;
	while(cin>>s>>n>>m&&m!=0&&n!=0&&s!=0)
	{
		if(n<m) swap(n,m);
		//cout<<s<<" "<<n<<" "<<m<<endl;
		bool vis[1000][1000]={0};
		if(s%2==1)
		{
			cout<<"NO"<<endl;
			continue;
		}
		memset(vis,0,sizeof(vis));
		queue<condition> a;
		condition t1={s,0,0,0};
		vis[0][0]=1;
		a.push(t1);
		while(!a.empty())
		{
			if(a.front().s==s/2&&s/2==a.front().a)
			{
				cout<<a.front().steps<<endl;
				break;
			}
			else
			{
				for(int i=0;i<6;i++)
				{
					int da,db,ds,dsteps;
					if(i==0&&a.front().s!=0&&a.front().a!=n)//s->a
					{
						int l=n-a.front().a;
						if(l>=a.front().s)//可以装入s中剩下的 
						{
							ds=0;
							da=a.front().s+a.front().a; 
						}
						else
						{
							da=n;
							ds=a.front().s-(n-a.front().a);
						}
						db=a.front().b;
						if(vis[da][db]==1)
						{
							continue;
						}
						dsteps=a.front().steps+1;
					}
					else if(i==1&&a.front().s!=0&&a.front().b!=m)//s->b
					{
						int l=m-a.front().b;
						if(l>=a.front().s)
						{
							ds=0;
							db=a.front().b+a.front().s;
						}
						else
						{
							db=m;
							ds=a.front().s-(m-a.front().b);
						}
						da=a.front().a;
						if(vis[da][db]==1) continue;
						dsteps=a.front().steps+1;
					}
					else if(i==2&&a.front().s!=s&&a.front().a!=0)//a->s
					{
						da=0;
						ds=a.front().a+a.front().s;
						db=a.front().b;
						if(vis[da][db]==1) continue;
						dsteps=a.front().steps+1;
					}
					else if(i==3&&a.front().s!=s&&a.front().b!=0)//b->s
					{
						db=0;
						ds=a.front().b+a.front().s;
						da=a.front().a;
						if(vis[da][db]==1) continue;
						dsteps=a.front().steps+1;
					}
					else if(i==4&&a.front().a!=0&&a.front().b!=m)//a->b
					{
						int l=m-a.front().b;
						if(l>=a.front().a)
						{
							da=0;
							db=a.front().a+a.front().b;
						}
						else
						{
							db=m;
							da=a.front().a-(m-a.front().b);
						}
						if(vis[da][db]==1) continue;
						ds=a.front().s;
						dsteps=a.front().steps+1;
					}
					else if(i==5&&a.front().b!=0&&a.front().a!=n)//b->a
					{
						int l=n-a.front().a;
						if(l>=a.front().b)
						{
							db=0;
							da=a.front().b+a.front().a;
						}
						else
						{
							da=n;
							db=a.front().b-(n-a.front().a);
						}
						if(vis[da][db]==1) continue;
						ds=a.front().s;
						dsteps=a.front().steps+1;
					}
					else continue;
					vis[da][db]=1;
					condition t1={ds,da,db,dsteps};
					a.push(t1);
					//cout<<ds<<" "<<da<<" "<<db<<" "<<dsteps<<" "<<i<<"\n";
				}
				
				a.pop();
			}
		}
		
		if(a.empty()) cout<<"NO"<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值