版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/83582957
题目链接 <http://codeforces.com/gym/101908/problem/B>
题意:
有一个100*100的棋盘,棋盘上有若干个棋子。两个人进行博弈,每次可以选择一个棋子进行移动,每次可以向左,或者向上,或者左上三个方向移动任意的距离。如果把任意一个棋子移到(0,0)点则获胜。给出n个棋子的坐标,问先手能不能获胜。
题解:
这题跟普通的Nim不一样。Nim是所有点都移动至(0,0),移动停止才会获胜。而这题是任意一点移动到(0,0)就获胜。
首先可以特判一下,如果一个点在(0,y)(x,0)(x,x)那么就肯定获胜,这些都是必胜态,我们可以把这三条线排除在外考虑。
那么(1,2)和(2,1)这两个点就可以当作Nim的终点,所有点都移动至这两个点移动就停止。这两个点Sg函数就是0,这样就形成了标准的有向Nim。更新Sg函数的时候跳过特判掉的三条线即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+7;
const int inf=1e4+7;
int sg[N][N];
bool s[N*N];
void getsg(){
for(int i=1;i<=100;i++){
for(int j=1;j<=100;j++){
if(j==i) continue;
memset(s,false,sizeof(s));
for(int k=1;k<=100;k++){
if(i-k>0&&i-k!=j) s[sg[i-k][j]]=true;
if(j-k>0&&i!=j-k) s[sg[i][j-k]]=true;
if(i-k>0&&j-k>0) s[sg[i-k][j-k]]=true;
}
for(int k=0;;k++){
if(!s[k]){
sg[i][j]=k;
break;
}
}
}
}
}
int main()
{
getsg();
int n;
scanf("%d",&n);
int x,y,ans=0,flag=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
if(x==y) flag=true;
ans^=sg[x][y];
}
if(flag||ans) printf("Y\n");
else printf("N\n");
}