1. 程式人生 > >【poj2068】Nim

【poj2068】Nim

而不是 con bre freopen rip 形式 inline class 感覺

Portal -->poj2068

Description

?  ?給你\(S\)個石子,有\(2n\)個人分成兩隊,編號為奇數的一隊,編號為偶數的一隊,\(2n\)個人按照編號從小到大的順序拿石子,所有人都拿過了就再從\(1\)號輪,編號為\(i\)的人一次可以拿\(x\in[1,a[i]]\)顆,拿到最後一顆石子的隊伍輸,判斷當前局面是否先手必勝

Solution

?  ?emmm今天做了幾道sg函數的題然後感覺這玩意很神秘

??  ?除了轉化成"有向圖遊戲"那樣的形式之後用異或和和\(mex\)\(sg\)以外,還有的題中\(sg\)的取值只有\(0\)\(1\)

兩種,可以直接判斷是否存在一個後繼局面的\(sg\)值為\(0\)(也就是先手必敗態),如果有就說明當前局面\(sg\)值為\(1\)(也就是先手必勝態),因為根據P-position(先敗)和N-position(先勝)的定義,可以移動到P-position的局面是N-position,所以直接這麽判就好了

??  ?當然你也還是可以轉成 一個有向圖遊戲,只要後繼局面中有\(0\),那麽取一下\(mex\)就只能是\(1\)了,一樣的

??  ?這題中比較容易想到的就是用"當前是誰準備取"和"當前還剩多少石子"來表示一個局面,那直接大力記憶化搜索就好了,邊界條件就是如果當前沒有石子了,那麽是先手必勝態

??  ?最後就是求\(nxt\)的時候模數記得是\(2n\)而不是\(n\)。。。

?  ?(一開始陷入了一個誤區。。就是覺得每個人的取石子上限不同,所以不是一個ICG,但其實ICG中只是要求移動集合(在這題裏也就是能移哪些石子)不與選手相關,並沒有限制具體操作)

?  ?

?  ?代碼大概長這個樣子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=9000;
int f[60][N],a[60];
int vis[N];
int n,m,S,mark;
int nxt(int x){return (x+1)%(2*n)==0?2*n:(x+1)%(2*n);}
int sg(int x,int stone){
    if (f[x][stone]!=-1) return f[x][stone];
    if (stone==0) return f[x][stone]=1;
    int tmp=nxt(x);
    for (int i=1;i<=a[x]&&i<=stone;++i){
        if (!sg(tmp,stone-i))
            return f[x][stone]=1;
    }
    return f[x][stone]=0;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    while (1){
        scanf("%d",&n);
        if (n==0) break;
        memset(f,-1,sizeof(f));
        scanf("%d",&S);
        mark=0;
        for (int i=1;i<=n*2;++i)scanf("%d",a+i);
        printf("%d\n",sg(1,S));
    }
}

【poj2068】Nim