Poj2960 S-Nim

Poj2960 S-Nim

Position:


List

Description

  • 大意:有n堆石子,每堆石子個數已知,兩人輪流從中取石子,每次可取的石子數x滿足x屬於集合S(k) = {s1,s2,s3…sk-1},問先拿者是否有必勝策略?
  • 普通Nim取石子游戲但加了一些限制條件,比如每次只能取S={s1,s2,s3……},就把前驅的條件改一下就行。

Knowledge

Sprague-Grundy Function-SG函數–博弈論
博弈論也是最近新學的知識,上面是一個寫得很好的博客。
簡單腦補:對於公平博弈(一般是NIM遊戲),我們有一個重要的工具————就是SG函數。
SG函數的定義:
必敗態的sg值爲0,其餘態的sg值爲其後繼狀態的sg值的mex和。
其中mex和操作(mex{a1,a2,a3,…,ar})的含義是a1,a2,a3,…,ar中最小的沒出現過的自然數。
而對於組合遊戲(就是由若干個子游戲組合而成,每個子游戲之間狀態獨立,每次操作任選一個子遊戲操作),其sg值爲所有子游戲sg值的異或和。如果一個狀態求得sg值爲0,則爲必敗態,否則爲必勝態。(證明略,大致是因爲先手總能通過一步使sg不爲0的狀態變爲0,而sg爲0的狀態只能變成sg不爲0的狀態,最後不能操作的狀態sg也爲0)而一般sg都是打表找規律,常用分析方法:(1) 等差分析(2) 等比分析(3) 特定數值位置分析(4) 奇偶位置分析。對於多組數據都不同就要暴力求,如本題。

Solution

分析:
1.可將問題轉化爲n個子問題,每個子問題分別爲:
從一堆x顆石子中取石子,每次可取的石子數爲集合S(k)中的一個數
2.分析(1)中的每個子問題,易得:SG(x)=mex(SG[(x-s[i]>0)])(k>=i>=1)
3.後面就是SG函數的應用,根據Sprague-Grundy Therem:g(G)=g(G1)^g(G2)^g(G3)^…^g(Gn)即遊戲的和的SG函數值是它的所有子游戲的SG函數值的異或,即SG(G) = SG(x1)^SG(x2)^…^SG(xn),故若SG(G)=0那麼必輸。

Notice

memset:①比for快②#include - cstring
map複雜度加一個log,對於加入的數少的情況用,else flag數組。

Code

// <S-Nim.cpp> - 08/03/16 20:18:06
// This file is made by YJinpeng,created by XuYike's black technology automatically.
// Copyright (C) 2016 ChangJun High School, Inc.
// I don't know what this program is.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
#include <cstdlib>
#include <cmath>
#define MOD 1000000007
#define INF 1e9
#define EPS 1e-10
using namespace std;
typedef long long LL;
const int MAXN=10010;
const int MAXM=100010;
inline int max(int &x,int &y) {return x>y?x:y;}
inline int min(int &x,int &y) {return x<y?x:y;}
inline int getint() {
    register int w=0,q=0;register char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')q=1,ch=getchar();
    while(ch>='0'&&ch<='9')w=w*10+ch-'0',ch=getchar();
    return q?-w:w;
}
int n,T,sg[MAXN],m,ans,s[MAXN];
inline int mex(int x){
    //map<int,bool>f;
    bool f[MAXN];
    memset(f,0,sizeof(f));//fast <cstring>
    int temp;
    for(int i=1;i<=n;i++){
        temp=x-s[i];
        if(temp>=0){
            if(sg[temp]==-1)sg[temp]=mex(temp);
            f[sg[temp]]=1;
        }
    }
    for(int i=0;;i++)if(!f[i])return i;
}
inline int SG(int x){
    if(sg[x]==-1)sg[x]=mex(x);
    return sg[x];
}
int main()
{
    freopen("S-Nim.in","r",stdin);
    freopen("S-Nim.out","w",stdout);
    while(n=getint(),n){
        for(int i=1;i<=n;i++)s[i]=getint();
        sg[0]=0;
        for(int i=1;i<MAXN;i++)sg[i]=-1;
        T=getint();
        while(T--){
            m=getint();ans=0;
            while(m--)ans^=SG(getint());
            if(ans)printf("W");else printf("L");
        }
        printf("\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章