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

来源:互联网 发布:手机淘宝积分怎么领 编辑:程序博客网 时间:2024/06/06 01:57

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 1111int 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");           }      }  }  
0 0
原创粉丝点击