【SG函数】Gym - 101908 - B. Marbles

版权声明:本文为博主原创文章,未经博主允许不得转载。 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");
}

猜你喜欢

转载自blog.csdn.net/monochrome00/article/details/83582957