题目描述
AKN玩游戏玩累了,于是他开始和同伴下棋了,玩的是跳棋!对手是wwx!这两位上古神遇在一起下棋,使得棋局变得玄幻莫测,高手过招,必有一赢,他们都将用最佳策略下棋,现在给你一个n*20的棋盘,以及棋盘上有若干个棋子,问谁赢?akn先手!
游戏规则是这样的:
对于一个棋子,能将它向右移动一格,如果右边有棋子,则向右跳到第一个空格,如果右边没有空格,则不能移动这个棋子,如果所有棋子都不能移动,那么将输掉这场比赛。
输入格式
第一行一个T,表示T组数据
每组数据第一行n,表示n*20的棋盘
接下来n行每行第一个数m表示第i行有m个棋子
随后跟着m个数pj表示第i行的棋子布局
输出格式
如果AKN能赢,则输出”YES”,否则输出”NO”
输入输出样例
输入 #1
2 1 2 19 20 2 1 19 1 18
输出 #1
NO YES
说明/提示
10%的数据T≤1,n≤1
另外10%的数据m≤1
100%的数据T≤100,n≤1000,m≤20,1≤pj≤20
By:Mul2016
思路
考虑阶梯Nim,每次可以把一个石堆的任意个棋子移到下一级阶梯。
那么显然就是奇数层的SG异或起来
因为偶数层的操作后手可以做对称的操作
那么考虑这题,发现棋子间会互相影响,不好表示,所有自然而然的可以想到转换模型,发现空格的个数是不会变的,可以看作阶梯Nim。
代码:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=50,M=1010; int SG[N],f[M]; int n,t,ans,len; int a[N],cnt[N]; int flag,top,tot; int main () { scanf("%d",&t); while(t--) { ans=0; scanf("%d",&n); for(int i=1; i<=n; i++) { memset(cnt,0,sizeof(cnt)); scanf("%d",&len); for(int j=1; j<=len; j++) { scanf("%d",&a[j]); cnt[a[j]]++; } tot=0,top=20,flag=0; while(cnt[top]) top--; for(; top; --top) if(!cnt[top]) { ans^=(flag?tot:0); flag^=1; tot=0; } else tot++; ans^=(flag?tot:0); } if(ans) printf("YES\n"); else printf("NO\n"); } return 0; }