poj 2960 S-Nim

取石子游戏的变体。题意:有m堆石子,事先给定一个集合S,每次只能从一堆中取石子,且取的石子个数必须是S中的数。没有石子可取或者不能按规则取石子即为输。给定初始情况,判断最开始的局面是必败L,还是必胜W局面。这题如果知道SG函数,就很好解了。由sg的基础知识可知,某一局面的sg值为0则对应P局面,反之对应N局面,所以把最终局面的sg值求出来就可以了。这里在用到一个定理: 设gi为子游戏Gi的SG函数,那么组合游戏G的SG函数为:g(x1,x2,x3,...xn) = g1(x1) xorg2(x2) xor... xorgn(xn)先考虑只有一堆的情况,在把各堆的sg值异或就可以了。

 

#include<iostream>

#include<cstdio>

#include<cstring>

#include<cmath>

#include<algorithm>

#define see(x) cout<<#x<<":"<<x<<endl;

using namespace std;

int s[105], k;

int sg[10005];



int dfs(int x){    //记忆化搜索确定SG值

    if(sg[x]!=-1) return sg[x];

    int i, f=0;

    bool vis[105] = {0};

    for(i=0;i<k;i++){

        if(x-s[i]>=0){

            dfs(x-s[i]);

            vis[sg[x-s[i]]] = true;

        }

    }

    for(i=0;i<105;i++){

        if(vis[i]==false){

            f = i;

            break;

        }

    }

    return sg[x] = f;

}

int main(){

    int m, n, i, j, l, t, ans;

    while(~scanf("%d",&k)&&k){

        for(i=0;i<k;i++){

            scanf("%d",&s[i]);

        }

        memset(sg,-1,sizeof(sg));

        sg[0] = 0;

        scanf("%d",&t);

        while(t--){

            ans = 0;

            scanf("%d",&l);

            for(i=0;i<l;i++){

                scanf("%d",&j);

                ans ^= dfs(j);

            }

            if(ans){

                cout<<"W";

            }

            else{

                cout<<"L";

            }

        }

        cout<<endl;

    }

    return 0;

}

 

 

你可能感兴趣的:(poj)