BZOJ 3895: 取石子

[Submit][Status][Discuss]
Description

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

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

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

3

3

1 1 2

2

3 4

3

2 3 5
Sample Output

YES

NO

NO
HINT

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

题解

找规律+观察可得,游戏的胜与负与操作次数有关,但一堆只有一个的石子要单独考虑
记忆化搜索+博弈论

代码

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 55;

int T,n,a[MAXN];
bool vis[MAXN][MAXN*1000],f[MAXN][MAXN*1000];

inline bool dfs(int x,int y){
    if(vis[x][y]) return f[x][y];
    vis[x][y]=1;
    if(x==0) return f[x][y]=y&1;  //没有一个石子的堆了 
    if(y==1) return f[x][y]=dfs(x+1,0);  //没有多个石子的堆 
    if(x>=2 && y && !dfs(x-2,y+3)) //将两堆一个的石子合并,之后还能其他堆合并
        return f[x][y]=1; 
    if(x>=2 && !y && !dfs(x-2,y+2)) //同上,但不能再与其他多余一个的堆合并 
        return f[x][y]=1;
    if(x && !dfs(x-1,y)) return f[x][y]=1;  //取走一个石子的一堆 
    if(y && !dfs(x,y-1)) return f[x][y]=1;  //取一个大于一个石子的一堆的一个 
    if(x && y && !dfs(x-1,y+1)) return f[x][y]=1;  //将一个石子与多个石子合并 
    return f[x][y]=0;
}

int main(){
    scanf("%d",&T);
    while(T--){
        int x=0,y=0;
        scanf("%d",&n);
        for(register int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            if(a[i]==1) x++;
            else y+=a[i]+1;
        }
        if(x==0) {
            if((y-1)&1) puts("YES");
            else puts("NO");    
        }
        else{
            if(dfs(x,max(y-1,0))) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}
posted @ 2018-06-09 19:58  Monster_Qi  阅读(217)  评论(0编辑  收藏  举报