HDU 1524 A Chess Game (SG函数模板题)

题意:在一个有向无环图上有n个顶点,每一个顶点都只有一个棋子,有两个人,每次根据这个图只能将任意一颗

棋子移动一步

,如果到某一步玩家不能移动时,那么这个人就输.


分析:本题是最典型的有向无环图的博弈,利用dfs把所有顶点的SG值都计算出来,然后对每个棋子的SG值进行

异或运算,如果为0就是先手必败,否则就是先手必胜.

如果某个人移动到出度为0的顶点,那么他必败,在这里首先介绍一下什么是SG函数.


Sprague-Grudy定理:

令N = {0, 1, 2, 3, ...} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。

结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数.

形式上:给定一个有限子集 S ⊂ N,令mex S(最小排斥值)为没有出现在S中的最小自然数。定义mex(minimal

excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}

=3、mex{2,3,5}=0、mex{}=0。

对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g如下:g(x)=mex{ g(y) | y是x

的后继 }。

性质:

(1)所有的终结点所对应的顶点,其SG值为0,因为它的后继集合是空集——所有终结点是必败点(P点即先

手必败点)。

(2)对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0——无论如何操作,从必败点(P点)都只能进

入必胜点(N点)//对手走完又只能把N留给我们。

(3)对于一个g(x)!=0的顶点,必定存在一个后继点y满足g(y)=0——从任何必胜点(N点)操作,至少有一

种方法可以进入必败点(P点)//就是那种我们要走的方法。


而求整个SG函数值的过程就是一个对有向无环图进行深搜过程.
其次解释一下为什么要异或。
这需要了解下Nim博弈,事实上我们可以利用SG函数转成Nim博弈。

Nim问题模型

然后解决它的思路是这样的

那么知道Nim博弈如何解决,我们如何将SG巧妙第转换为nim呢?

以上内容均来自《挑战程序设计竞赛》

所以,问题就解决啦,先DFS出SG函数(注意要用记忆化搜索哦,否则很容易超时),然后算出所有SG的异或值即可。

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn=1005;
vector<int>G[maxn];
int n,dp[maxn];
int dfs(int u)
{
    if(dp[u]!=-1) return dp[u];
    int vis[maxn];
    fill(vis,vis+n,0);
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        dp[v]=dfs(v);
        vis[dp[v]]=1;
    }
    for(int i=0;i<n;i++)
        if(vis[i]==0) return i;
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(cin>>n)
    {
        for(int i=0;i<=n;i++) G[i].clear(),dp[i]=-1;//清空邻接矩阵以及初始化记忆化数组
        for(int i=0;i<n;i++)
        {
            int m,x;
            cin>>m;
            while(m--)
            {
                cin>>x;
                G[i].push_back(x);//用邻接表存有向图
            }
        }
        int q;
        while(cin>>q,q)
        {
            int x,f=0;
            while(q--)
            {
                cin>>x;
                f^=dfs(x);//算出所有数异或的结果
            }
            if(f) cout<<"WIN"<<endl;
            else cout<<"LOSE"<<endl;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值