向前走莫回头❤

【bzoj 3895】取石子(博弈+记忆化搜索)

3895: 取石子

Time Limit: 1 Sec  Memory Limit: 512 MB
Submit: 267  Solved: 130
[Submit][Status][Discuss]

Description

Alice和Bob两个好朋含友又开始玩取石子了。游戏开始时,有N堆石子
排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中任选一个:
·从某堆石子中取走一个
·合并任意两堆石子
不能操作的人输。Alice想知道,她是否能有必胜策略。

Input

第一行输入T,表示数据组数。
对于每组测试数据,第一行读入N。
接下来N个正整数a1,a2…an,表示每堆石子的数量。

Output

对于每组测试数据,输出一行。
输出YES表示Alice有必胜策略,输出NO表示Alice没有必胜策略。

Sample Input

2
3
1 1 2
2
3 4
3
2 3 5

Sample Output

YES
NO
NO

HINT

100%的数据满足T<=100,  N<=50. ai<=1000

Source

[Submit][Status][Discuss]

【题解】【博弈+记忆化搜索】

【 假设每堆石子的数量都>1,那么我们定义操作数b为当前石子总数+当前堆数-1,若b为奇数,则先手必胜,否则后手必胜

1.如果先手选择合并两堆石子,那么每堆石子的个数依然大于1,tot变为奇数.】
【2.如果先手选择从一堆石子数大于2的堆中拿走一枚石子,那么同上每堆石子个数依然大于1,tot变为奇数。】 
【3.如果先手选择从一堆石子数等于2的堆中拿走一枚石子,那么后手可以合并剩下的1枚石子到任意一个堆(堆数-1,石子数,先手依旧面临偶数)。那样tot的奇偶性不变,每堆石子的个数依然大于1. 那么为什么后手不会选择把剩下的一个取走呢?因为取走后堆数-1,石子数-1,这样奇偶性就变了,就把必胜太留给了对手,博弈的两个人一定是聪明的所以他不可能这么选择)

 【下面考虑含有1的情况:】

   [1) 从石子数为1的堆里取一个; 

    2) 将两堆石子数为1的堆合并;  

    3) 把石子数为1的堆与一个石子数不为1的堆合并 ; 

    4) 从石子数不为1的堆里取走一个]

【共四种情况,记忆化搜索。不过要记住一点:每组数据间不重置


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,t;
int f[110][100010];
int dfs(int a,int b)
{
	if (f[a][b]!=-1) return f[a][b];
	if(!a) return f[a][b]=b%2;//如果不存在石子数为1的堆,那么操作数为奇数,先手必胜;反之,先手必败 
	if(b==1) return f[a][b]=dfs(a+1,0);
	int t=2;
	if(a) t=min(t,dfs(a-1,b));//从石子数为1的堆里取一个 
	if(a>1) t=min(t,dfs(a-2,b+2+(b?1:0)));//将两堆石子数为1的堆合并
	if(a&&b) t=min(t,dfs(a-1,b+1));//把石子数为1的堆与一个石子数不为1的堆合并 
	if(b) t=min(t,dfs(a,b-1));//从石子数不为1的堆里取走一个
	if(!t) f[a][b]=1;
	 else f[a][b]=0;
	return f[a][b]; 
}
int main()
{
	int i,j;
	scanf("%d",&t);
	memset(f,-1,sizeof(f));
    for(i=1;i<=t;++i)
	 {
	 	scanf("%d",&n);
	 	int a=0,b=0;
	 	for(j=1;j<=n;++j)
	 	 {
	 	 	int x;
	 	 	scanf("%d",&x);
	 	 	if(x==1) a++;
	 	 	 else b+=x+1;
		  }
		if(b>0) b--;
		if(dfs(a,b)==1) printf("YES\n");
		 else printf("NO\n");
	 }	
}


posted @ 2016-08-09 19:42  lris0-0  阅读(130)  评论(0编辑  收藏  举报
过去的终会化为美满的财富~o( =∩ω∩= )m