ZOJ 3452 Doraemon's Stone Game(博弈)

ZOJ的博弈题目,主角是哆啦A梦和多啦美,下面简称为A和B

游戏背景是两个人相互取石子,石子有黑白两种颜色,每个人只能去对应颜色的石子。谁不能操作就算输。

游戏规则

1 A只能拿白色的石头,也就是w的石头,B只能拿黑色的石头,也就是b的石头。

2每个人每次只能拿走一块石头

3 每堆石头块最多有两块石头,玩家有两种取石头的方式,可以从石头块的上面拿,也可以从石头快的下面拿。如果从下面拿,那么这一堆石头块就会消失,此时最底下上面那块石头会消失。这是关键。

 

题目的输入数据:首先输入石头块的种类N,简单排列一下知道有 b, w wb,bw,aa,bb这六种。

字符串从左到右代表石头块从上到下的顺序。

接下来N行,每一行一个字符串和数字,代表石头块的种类和数量。

 

最后问你A先手和后手的胜负情况。

 

首先分析下情况:在给定的情况下,每个人可以操作的步数(或者说回合数)基本可以确定。

那么谁的可操作数大,那么谁就可以在对方用尽操作步数以后获胜。

那么最后问题就简化为,说的可操作步数多,谁赢。这一点比较容易想到。

解题关键是上面说的关键部分,当拿走最底下一块以后这堆石头会消失,那么对方就会少一种可以操作的方式。

这样每次可操作步数,会受到对方选择而改变。

但这也是解题的思路。

当对方拿走wb这堆石头,我就少了一个w可以拿,那我的可以操作的步数就少了,这样的情况对我不利。那么轮到我操作的时候,我就拿bw的石堆。这样对方可以操作的步数也少了。这样情况就趋于平衡。

最后场面只会有两种情况,纯色的黑白石堆和多的wb石堆或者bw石堆。

这时候,情况就大大简化,统计下双方可以操作的步数就可以知道谁赢。

 

假设最后只有wb有X堆,我先手的情况下可以操作(X+1)/2次,后手的情况下可以操作X/2次;

假设最后只有bw有Y堆,我先手的情况下可以操作(Y+1)/2次,后手的情况下可以操作Y/2次。

到这里,代码就比较容易写了。混合的情况如上面所说的,纯色的情况下,一个石头算一次操作步数。

其他人的博客我也看过,ans1和ans2的处理是把多种情况混合在一起,这样难得理解点。

我尽量把代码写的通俗易懂,更容易理解。

#include<iostream>
#include<cstdio>
#include<functional>
#include<algorithm>
#include<cstring>
using namespace std;
long long bb,ww,wb,bw;
char s[10];
int n;
void fprint(long long x,long long y)//先手的情况
{
	if(x>y)
		printf("win ");
	else
		printf("lose ");
}
void sprint(long long x,long long y)//后手的情况
{
	if(x>=y)
		printf("win\n");
	else
		printf("lose\n");
}
int main()
{
	int i,j,len;
	long long ans1,ans2,a;
	while(scanf("%d",&n)!=EOF)
	{
		bb=ww=wb=bw=0;//统计数据清零
		for(i=1;i<=n;i++)
		{
			scanf("%s %lld",&s,&a);//输入
			len=strlen(s);
			if(len==1)//统计单个石头的情况
			{
				if(s[0]=='b')
					bb+=a;
				else
					ww+=a;
			}
			else//统计两个石头的情况
			{
				if(s[0]=='b'&&s[1]=='b')
					bb+=2*a;
				else if(s[0]=='w'&&s[1]=='w')//纯色石子统计
					ww+=2*a;
				else if(s[0]=='b'&&s[1]=='w')
					bw+=a;
				else if(s[0]=='w'&&s[1]=='b')//混合石子统计
					wb+=a;
			}
		}
		if(bw==wb)//计算最后是哪一种混合石堆剩下
			bw=wb=0;
		else if(bw>wb)
			bw-=wb, wb=0;
		else
			wb-=bw, bw=0;

		if(bb==ww)//只用算多出的部分,可以缩减一下数据范围
			bb=ww=0;
		else if(bb>ww)
			bb-=ww, ww=0;
		else
			ww-=bb, bb=0;

		if(bw==wb)
		{
			fprint(ww,bb);
			sprint(ww,bb);
		}
		else if(bw>wb)//ans1值没有改变,可以省略一个,这样写容易看
		{//wb=0
			ans1=ww+bw;//bw这种石堆都可以拿
			ans2=bb+bw/2;//后手只能拿一半
			fprint(ans1,ans2);//输出先手的情况

			ans1=ww+bw;//我后手
			ans2=bb+(bw+1)/2;//对于别人来说是先手
			sprint(ans1,ans2);//输出后手的情况
		}
		else//ans2值没有改变,可以省略一个,这样写容易看
		{//bw=0
			ans1=ww+(wb+1)/2;//理由同上
			ans2=bb+wb;
			fprint(ans1,ans2);

			ans1=ww+wb/2;
			ans2=bb+wb;
			sprint(ans1,ans2);
		}
	}
	return 0;
}


其他人的博客,写法大致相同,ans1和ans2处理

全部评论

相关推荐

10-09 22:05
666 C++
找到工作就狠狠玩CSGO:报联合国演讲,报电子烟设计与制造
点赞 评论 收藏
分享
牛舌:如果我不想去,不管对方给了多少,我一般都会说你们给得太低了。这样他们就会给下一个offer的人更高的薪资了。
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务