有趣的nim game

刷题目前,请先了解nim game和sg函数的部分知识

hdoj1730.Northcott Game

当棋子紧贴着时先手必输,n行紧贴的棋子先手必输,原题相当于取完n行两棋子中间的部分构成n行紧贴的局面,等价于nim game

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
int n,m,k;
signed main(){
    int T,x,y;
    while (scanf("%d%d",&n,&m)==2){
        int ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            ans^=abs(x-y)-1;
        }
        if (ans==0)puts("BAD LUCK!");
        else puts("I WIN!");
    }
    return 0;
}

hdoj6886.Tic-Tac-Toe-Nim

取完2个格子后,必存在a 0 b类似的行列,要使其构成1 0 1的必输局面,也就是(a-1)^(b-1)类似的nim game
例 0 2 3 为(2-1)^(3-1)^(4-1)^(6-1)^(7-1)^(8-1)^9
4 0 6
7 8 9

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[4][4];
int main(){
    int T;
    scanf("%d",&T);
    while (T--){
        for (int i=1;i<=3;i++){
            for (int j=1;j<=3;j++){
                scanf("%d",&a[i][j]);
            }
        }
        int tot=0;
        for (int i=1;i<=3;i++){
            for (int j=1;j<=3;j++){
                int ans=1;
                for (int k=1;k<=3;k++){
                    for (int p=1;p<=3;p++){
                        if (k==i||p==j)continue;
                        int r=0;
                        for (int x=1;x<=3;x++){
                            for (int y=1;y<=3;y++){
                                if (x==i&&y==j)continue;
                                if (x==k&&y==p)continue;
                                if (x==k||x==i||y==p||y==j)r^=a[x][y]-1;
                                else r^=a[x][y];
                            }
                        }
                        if (r==0)ans=0;
                    }
                }
                if (ans==1)tot++;
            }
        }
        cout<<tot<<endl;
    }
    return 0;
}

hdoj3032.Nim or not Nim?

Lasker’s Nim游戏

  1. 在一堆中随便取
  2. 1堆变成2堆
    sg(4k)=4k-1;
    sg(4k+1)=4k+1;
    sg(4k+2)=4k+2;
    sg(4k+3)=4k+4;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
int n,m,k;
signed main(){
    int T,x,y;
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        int ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&x);
            if (x%4==0)x--;
            else if (x%4==3)x++;
            ans^=x;
        }
        if (ans==0)puts("Bob");
        else puts("Alice");
    }
    return 0;
}

hdoj5795.A Simple Nim

Lasker’s Nim游戏

  1. 在一堆中随便取
  2. 1堆变成3堆
    sg(8k)=8k-1;
    sg(8k+7)=8k+8;
    其他一样
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
int n,m,k;
signed main(){
    int T,x,y;
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        int ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&x);
            if (x%8==7)x++;
            else if (x%8==0)x--;
            ans^=x;
        }
        if (ans==0)puts("Second player wins.");
        else puts("First player wins.");
    }
    return 0;
}

hdoj1536.S-Nim

sg函数
再一堆中取给定数组个数的数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+10;
const int mod=1e9+7;
int n,m,k;
int s[N],sg[N],ha[N];
void get_sg(int n){
    sg[0]=0;
    int flag=1;//时间戳
    memset(ha,0,sizeof ha);
    for (int i=1;i<=n;i++){
        for(int j=1;s[j]<=i&&j<=k;j++)
            ha[sg[i-s[j]]]=flag;
        for(int j=0;j<=n;j++){
            if(ha[j]!=flag){
                sg[i]=j;break;
            }
        }
        flag++;
    }
}
signed main(){  
    int T,x,y;  
    while (scanf("%d",&k)==1&&k){
        for (int i=1;i<=k;i++)scanf("%d",&s[i]);
        sort(s+1,s+1+k);
        get_sg(10000);
        string ss="";
        scanf("%d",&m);
        while(m--){
            scanf("%d",&n);
            int ans=0;
            for (int i=1;i<=n;i++){
                scanf("%d",&x);ans^=sg[x];
            }
            if (ans==0)ss+="L";
            else ss+="W";
        }
        cout<<ss<<endl;
    }
    return 0;
}

hdoj5011.Game

  1. 取一堆的任意个
  2. 取一堆的任意个,之后把该堆分成2堆
    如果之前异或和为0,在2操作后异或和必然不为0,所以还是nimgame
    推广:1.分成任意堆仍是nimgame
    2.只有2操作
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
int n,m,k;
signed main(){
    int T,x,y;
    while (scanf("%d",&n)!=EOF){
        int ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&x);
            ans^=x;
        }
        if (ans==0)puts("Lose");
        else puts("Win");
    }
    return 0;
}

hdoj1404.Digital Deletions

看起来像nim的博,此处的sg只是必胜必败局面的转换

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,m,k;
int sg[N];
char s[N];
int change(char *s){
    int len=strlen(s),ans=0;
    for (int i=0;i<len;i++){
        ans=ans*10+s[i]-'0';
    }
    return ans;
}
void get_sg(){
    sg[0]=1;
    for (int i=1;i<=999999;i++){
        for (int j=1;j<=i;j*=10){
            int t=i/j%10;
            for (int k=1;k<=t;k++){
                if (j*10>i&&k==t)sg[i]|=0;
                else sg[i]|=!sg[i-j*k];
            }
            if (t==0)sg[i]|=!sg[i/j/10];
            if (j>i)break;
        }
    }
}
signed main(){
    int T,x,y;
    get_sg();
    while (scanf("%s",s)!=EOF){
        if (s[0]=='0')puts("Yes");
        else if (sg[change(s)])puts("Yes");
        else puts("No");
    }
    return 0;
}

hdoj5996.dingyeye loves stone

nimgame进阶 阶梯博弈
深度为奇数的进行异或和
因为当移动深度为偶数,可以将同样部分往前移动
当移动深度为奇数,可以同nimgame那样往前移动

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,k;
vector<int>v[N];
int dep[N];
void dfs(int x,int fa){
    dep[x]=dep[fa]+1;
    for (int i:v[x]){
        if (i==fa)continue;
        dfs(i,x);
    }
}
signed main(){
    int T,x,y;
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        for (int i=2;i<=n;i++){
            scanf("%d",&x);x++;
            v[i].push_back(x);v[x].push_back(i);
        }
        dep[0]=-1;
        dfs(1,0);
        int ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&x);
            if (dep[i]&1)ans^=x;
        }
        if (ans==0)puts("lose");
        else puts("win");
        for (int i=1;i<=n;i++)v[i].clear();
    }
    return 0;
}

hdoj3389.Game

阶梯博弈
(A+B)%30,(A+B)%21 <=> (A+B)%63

如图,图中的0-5代表%6
0-5,连线的代表可以互相到达,最终点必定为1,3,4,因为0不能到达
而0,2,5必定是奇数步,形成了阶梯博弈

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
int n,m,k;
int a[N];
signed main(){
    int T,x,y;
    scanf("%d",&T);
    for (int cas=1;cas<=T;cas++){
        scanf("%d",&n);
        int ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&x);
            if (i%6==0||i%6==2||i%6==5)ans^=x;
        }
        if (ans)printf("Case %d: Alice\n",cas);
        else printf("Case %d: Bob\n",cas);
    }
    return 0;
}

hdoj4315.Climbing the Hill

阶梯博弈
把间距看成球的个数,往前走就是把球往后移动
如果k=n就和阶梯博弈一摸一样,但这里k不一定为n
特殊情况:
1.k=1,先手必赢
2.n&11且k2,a[1]--,因为a[1]不能到达山顶

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
int n,m,k;
int a[N];
signed main(){
    int T,x,y;
    while (scanf("%d%d",&n,&k)!=EOF){
        for (int i=1;i<=n;i++)scanf("%d",&a[i]);
        if (k==1)puts("Alice");
        else{
            int ans=0;
            if (n&1){
                if(k==2)a[1]--;
                ans^=a[1];
                for(int i=3;i<=n;i+=2)ans^=(a[i]-a[i-1]-1);
            }
            else{
                for(int i=2;i<=n;i+=2)ans^=(a[i]-a[i-1]-1);
            }
            if(ans)puts("Alice");
            else puts("Bob");
        }
    }
    return 0;
}
posted @ 2020-08-27 16:56  Rynar  阅读(206)  评论(0)    收藏  举报