LA 3668 A Funny Stone Game(博弈,SG定理)

LA  3668 A Funny Stone Game

題意:有n堆石子,編號爲0~n-1。第i堆有Si個石子 ,兩個人輪流操作。每次可以選3堆i,j,k(i<j<=k),且第i堆必須又石子,從第i堆中取出一個石子,往第j和第k堆各加入一個石子(若j==k ,加兩個石子)。 不能操作的人輸。分析先手是必敗還是必勝 。若必勝要求給出字典序最小的必勝操作(i,j,k)。

分析:

先考慮這樣一個簡單點的遊戲:[ 記爲game(i) ]

同樣有n個堆,但只有其中一個堆(設爲i)有一個石子,操作規則和上面的遊戲相同。問先手必勝還是必敗?

如果你熟悉SG定理,一定可以馬上想到解決方法。

SG[ i ]   = mex{ SG[ j ] ^ SG[ k ] }     ,   i > j >= k


再回到原來的問題,其實每個石子不就可以看做一個上面的簡單遊戲嗎 , 這樣再用一次SG定理就可以得到整個遊戲的SG值了!  ,至於字典序最小的操作 , 從小到大一次枚舉即可 ,因爲n<=23 ,毫無壓力。


參考代碼:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int sg[50];
int mex(vector<int>&S){
    sort(S.begin(),S.end());
    if(S[0] > 0) return 0;
    int s_size = S.size();
    for(int i=1;i<s_size;i++) {
        if(S[i]-S[i-1] > 1) return S[i-1]+1;
    }
    return S[s_size-1] + 1;
}
int SG(int x){
    if(sg[x] != -1) return sg[x];
    vector<int>S;
    for(int i=0;i<x;i++)
        for(int j=0;j<=i;j++)
            S.push_back(SG(i)^SG(j));
    return sg[x] = mex(S);
}
int s[50],n;
int sum_sg(){
    int g = 0;
    for(int i=1;i<n;i++){
        if(s[i]&1) g^=SG(i);
    }
    return g;
}
int main()
{
    memset(sg,-1,sizeof(sg));
    sg[0] = 0 ,sg[1] = 1;
    int cas = 1;
    while(scanf("%d",&n)!=EOF && n){
        for(int i=n-1;i>=0;i--){ //堆的序號翻轉過來了
            scanf("%d",&s[i]);
        }
        bool ok = false;
        int i,j,k;
        for(i=n-1;i>=1;i--) if(s[i]>0){
            for(j=i-1;j>=0;j--){
                for(k=j;k>=0;k--){
                    s[i]-- , s[j]++ , s[k]++;
                    if(sum_sg()==0) {ok=true; goto output ;}
                    s[i]++ , s[j]-- , s[k]--;
                }
            }
        }
        output :;
        printf("Game %d: ",cas++);
        if(ok) printf("%d %d %d\n",n-i-1,n-j-1,n-k-1);
        else printf("-1 -1 -1\n");
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章