博弈论(一)——#10243. 「一本通 6.7 例 3」移棋子游戏

题目链接:https://loj.ac/problem/10243
解题思路
因为是一张有向无环图,正好对应有向图游戏和模型,可以利用sg函数异或和求解。我们发现,出度为0的点,不能再移动,是P点,所以其sg函数必是0。其它点的sg函数都可以由其儿子节点推出。所以我们对整张图dfs就可以求得所有点的sg函数(图可能要多次dfs)。然后求对应点sg函数异或和,非0则先手必胜,0则先手必败。
AC代码

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2e3+5;
const int maxm=6e3+5;
const int inf=0x3f3f3f3f;
struct node
{
    int to,next;
}edge[maxm];
int head[maxn];
int num[maxn];
int sg[maxn];
bool vis[maxn];
int cnt;
int n,m,k;
void add(int x,int y)
{
    edge[++cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
int dfs(int u)
{
    bool flag=0;
    if(sg[u]!=inf)
    return sg[u];
    bool vis[maxn];
    memset(vis,0,sizeof(vis));
    int ma=-1;
    for(int i=head[u];i;i=edge[i].next)
    {
        flag=1;
        int v=edge[i].to;
        int w=dfs(v);
        ma=max(ma,w);
        vis[w]=1;
    }
    if(!flag)
    sg[u]=0;
    else
    {
        for(int i=0;i<=ma+1;++i)
        if(!vis[i])
        {
            sg[u]=i;
            break;
        }
    }
    return sg[u];
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    memset(sg,inf,sizeof(sg));
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    for(int i=1;i<=n;++i)
    {
        if(sg[i]==inf)
        dfs(i);
    }
    for(int i=1;i<=k;++i)
    scanf("%d",&num[i]);
    int ans=0;
    for(int i=1;i<=k;++i)
    {
        ans^=sg[num[i]];
    }
    if(ans)
    puts("win");
    else
    puts("lose");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/108433347
今日推荐