左右手抓石子堆博弈

左右手抓石子堆博弈

题目描述

面前有n堆石子(编号从1到n),每堆石子有 a i a_i 个,每个回合,可以取走同一堆石子中的任意个,直到这堆石子全部取完,才会取下一堆,(取完第一堆才能取第二堆,以此类推)取走最后一颗石子的获胜
若右手先取,是否存在必胜策略,若存在,输出"YES",否则输出"NO"。
在这里插入图片描述

分析

当只有一堆石子时,不管这一堆石子有多少颗,只要是掌握了主动权(先手的一方)必胜.因为将这堆石子全部拿走一定能拿到最后一颗
于是,我们要做的是:争取取最后一堆石子时的主动权

当有两堆以上的石子时,有两种情况:

  • 一颗石子
  • 两颗及以上的石子

我方先手且这一堆只有一颗石子时,我方必定被迫让出主动权
我方先手且这一堆且有两颗石子以上时,我方有两个选择:第一,全部拿完,让出主动权;或者,第二,拿这一堆石子的 a i 1 a_i-1 颗石子,对方被迫拿最后一颗,我方保持主动权

我们将数字分为1和大于等于2(用X表示)的两组

举个例子:石子堆的情况为1 2 X X
在这里插入图片描述
由于第一堆是1,所以我方被迫交换先后手,此时对手获得主动权,根据上面分析,对手一定会选择自己控制主动权,使得我方失败

看第二种情况:2 1 X X
在这里插入图片描述
由于后面有1,会使我们的主动权强制交换,所以我们选择在有两颗石子时交出主动权,对手只有一颗石子可拿,再把主动权交还给我们,这样一来,我们就赢了

那么,当后边的石子堆有N个1颗的石子堆呢?当N为偶数,相当于没有;当为奇数,转换为上面两种情况讨论

于是,我们可以总结出以下结论:
只有当我们遇到第一堆只有一颗石子的情况下我们才会输(当有多个1时进行等效化简成我们上面两种情况)
也就是说,只要我们先手,没有一堆1颗保持主动即可获胜;有时,只要它前面有任意一堆X,我们就可以获胜

整理可得代码

代码

#include<bits/stdc++.h>
using namespace std;

int a[100005];

int main(){
	int n;//有几堆石子 
	cin>>n;
	bool w=true;//我方能否获胜 
	for(int i=1;i<=n;i++){//读入每堆石子个数 
		cin>>a[i];
	}
	for(int i=1;i<n;i++){//检查是否是第一堆就是一颗石子的情况 
	//i<n是因为当只有一堆,不管是一颗还是多颗都全部取走,不属于检查范围
		if(a[i]!=1)	break;//见分析,只要有X,就能获胜 
		w^=1;//如果没有,就对w取反 
	}
	if(w==1)	printf("YES\n");
	else		printf("NO\n");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/beta___/article/details/107902002
今日推荐