题意:给定一个n行20列的棋盘,并给定棋子(多个)的初始位置,两个人轮流移动棋子,最后不能移动棋子者输 移动规则:一次只能移动一个棋子,如果该棋子右边无棋子,则可以向右移动一格,如果该棋子右边有棋子(一个或多个连续),则该棋子可以跳过右边这些的棋子,向右移动到最靠近该棋子的空格。
思路:阶梯博弈变形:
阶梯博弈(Staircase Nim):
博弈在一列阶梯上进行,每个阶梯上没有石子或放着若干个石子。两个人进行阶梯博弈,每一步则是将阶梯(不是第一层的阶梯)上的若干个石子移到(某些指定的)低阶的阶梯去,最后没有石子可以移动的人输。
阶梯博弈可以利用游戏的限制转化成Nim解决:由于某个阶梯上的石子移动到不能移动时所需的步数要么是奇数,要么是偶数。对于偶数步,对方用哪些石子走一步你也用哪些石子走一步,从而不影响游戏结果。所以只对奇数阶的石子求Nim就行。
该题博弈在一个N个格子的方块上进行,每个方块上至多有一个石子,每次可以选择一个石子向右移动到最近的空位,不能移动石子的人输,如下图所示:
设函数F[i]表示第i个石子移动到不能移动所需要的步数。
然后我们来观察某一次移动,假设图中的2跳到了4的后面,我们可以来观察一下所有石子的F函数有什么变化:
首先对于1及其之前的石子,它们的F函数显然不变。
同样,对于移动后的2之后的石子,它们的F函数也不变。
那么变化的就是2,3,4这三个石子的F值,变化了多少?每一个减少了1
那么,如果我们把这个函数F[]看做台阶的阶数的话,乃是不是觉得有一点熟悉的感觉了~
没错!就是阶梯博弈
这里看似只能移动一个棋子,但是实际上导致了多个棋子的F值减少一,即相当于将某台阶上任意个棋子移动到下一台阶,所以此题只需将F值为奇数的棋子进行统计,然后对F值相同的棋子的数目取Nim即可
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int x=0;//注意该类题异或时初始值应该为0
for(int i=0;i<n;i++){
int qipan[21];
int f[21];
int shizishu[21];
int shizishu_cnt=0;
memset(shizishu,0,sizeof(shizishu));
memset(qipan,0,sizeof(qipan));
memset(f,0,sizeof(f));
int m;
scanf("%d",&m);
for(int j=0;j<m;j++){
int p;
scanf("%d",&p);
qipan[p]=1;
}
for(int k=1;k<=20;k++){
if(qipan[k]==1){
for(int l=k+1;l<=20;l++)
if(qipan[l]==0) f[k]+=1;
}
if(f[k]%2==1) shizishu[f[k]]++;
}
for(int y=1;y<=20;y++)
if(shizishu[y]!=0) x^=shizishu[y];
}
if(x!=0) printf("YES\n");
else printf("NO\n");
}
return 0;
}
Chess
The first line contains an integer T(T≤100) , indicates the number of test cases.
For each test case, the first line contains a single integer n(n≤1000) , the number of lines of chessboard.
Then n lines, the first integer of ith line is m(m≤20) , indicates the number of chesses on the ith line of the chessboard. Then m integers pj(1≤pj≤20) followed, the position of each chess.
2 1 2 19 20 2 1 19 1 18
NO YES