POJ 2425 && HDU 1524 A Chess Game(博弈论)

Description
在一个有向无环图上放着n个棋子,Alice和Bob每次可以选择其中一枚棋子沿有向边移动到任意一个后继点(一个点上可以同时有多枚棋子),最终不能移动者输。问如果两人都足够机智,先手Alice是否有必胜策略
Input
多组输入,每组用例第一行为图中点数n(点的标号从0~n-1),之后n行每行首先输入一个整数m表示与该点相连的点数,之后m个整数表示这m个点的下标,在每张图输入完毕后会有多组游戏,每组游戏占一行,每组游戏首先输入棋子个数m,之后m个整数表示这m枚棋子的位置,游戏的输入以m=0结束,以文件为结束全部输入
Output
对于每组游戏,如果先手有必胜策略则输出WIN,否则输出LOSE
Sample Input
4
2 1 2
0
1 3
0
1 0
2 0 2
0

4
1 1
1 2
0
0
2 0 1
2 1 1
3 0 1 3
0
Sample Output
WIN
WIN
WIN
LOSE
WIN
Solution
经典S-Nim游戏,这类博弈游戏的突破口往往在sg值的定义上,首先解决一枚棋子后即可解决多枚棋子,因为只要求出每枚棋子初始位置的sg值之后取异或判断是否为0即可,此题对于没有后继的点,定义其sg值为0,对于有后继的点,定义其sg值为其所有后继点sg值中没有出现过的最小自然数,这样一遍dfs即可求出所有点的sg值
Code

#include<iostream> 
#include<cstring> 
#include<cstdio> 
using namespace std;  
#define maxn 1111
int M[maxn][maxn];  
int n;  
int sg[maxn];  
int dfs(int x)  
{  
    if(sg[x]!=-1) 
        return sg[x];  
    bool zrs[maxn];//自然数集合 
    memset(zrs,false,sizeof(zrs));//初始化 
    for(int i=0;i<n;i++)  
        if(M[x][i]==1)//将子节点的sg值标记 
            zrs[dfs(i)]=true;    
    for(int i=0;;i++)//找到其子节点sg值中没出现过的最小自然数即为该点sg值 
        if(zrs[i]==0) 
            return sg[x]=i;  
}  
int main()  
{    
    while(~scanf("%d",&n))  
    {  
        //初始化 
        memset(M,-1,sizeof(M));  
        memset(sg,-1,sizeof(sg));  
        for(int i=0;i<n;i++)  
        {  
            int x,y;
            scanf("%d",&x);  
            if(x==0)//没有出度的点sg值为0 
                sg[i]=0;  
            else
            {
                while(x--) 
                {  
                    scanf("%d",&y);  
                    M[i][y]=1;  
                }  
            }
        }    
        int m;
        while(scanf("%d",&m),m)  
        {
            int ans=0;  
            while(m--)  
            {  
                int q;
                scanf("%d",&q);  
                ans^=dfs(q);  
            }  
            if(ans!=0) printf("WIN\n");  
            else printf("LOSE\n");   
        }  
    }  
}  

你可能感兴趣的:(POJ 2425 && HDU 1524 A Chess Game(博弈论))