首页 > 其他分享 >HDU1524(博弈--有向无环图SG函数)

HDU1524(博弈--有向无环图SG函数)

时间:2023-05-31 19:04:42浏览次数:39  
标签:HDU1524 -- head 环图 int cnt 顶点 SG mex


题目:http://acm.hdu.edu.cn/showproblem.php?pid=1524


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

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


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

为0就是先手必败,否则就是先手必胜.


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


对于给定的有向无环图,定义图中每个顶点的Sprague-Grundy函数g如下:g(x) = mex{ g(y) | y是x的后继 }。

mex(x)表示最小的不属于这个集合的非负整数。例如:mex{0,1,2,4} = 3、mex{2,3,5} = 0、mex{ } = 0。


SG函数的性质:首先,所有终结点所对应的顶点,也就是出度为0的顶点,其SG值为0,因为它的后继集合是空集。然后对于一

个g(x) = 0的顶点x,它的所有后继y都满足g(y)!=0。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0.


而求整个SG函数值的过程就是一个对有向无环图进行深搜过程.



#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
const int N = 2005;

int head[N];
int n,m,cnt;

struct Edge
{
    int to;
    int next;
};

Edge edge[N*N];
int SG[N];

void Init()
{
    cnt = 0;
    memset(SG,-1,sizeof(SG));
    memset(head,-1,sizeof(head));
}

void add(int u,int v)
{
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

int mex(int x)
{
    if(SG[x] != -1) return SG[x];
    bool vis[N];
    memset(vis,0,sizeof(vis));
    for(int i=head[x];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        SG[v] = mex(v);
        vis[SG[v]] = 1;
    }
    for(int i=0;;i++)
    if(!vis[i]) return i;
}

int main()
{
    while(~scanf("%d",&n))
    {
        Init();
        for(int i=0;i<n;i++)
        {
            scanf("%d",&m);
            while(m--)
            {
                int u;
                scanf("%d",&u);
                add(i,u);
            }
        }
        int k;
        while(~scanf("%d",&k))
        {
            if(k == 0) break;
            int ans = 0;
            while(k--)
            {
                int x;
                scanf("%d",&x);
                ans ^= mex(x);
            }
            if(ans) puts("WIN");
            else    puts("LOSE");
        }
    }
    return 0;
}





标签:HDU1524,--,head,环图,int,cnt,顶点,SG,mex
From: https://blog.51cto.com/u_16146153/6388987

相关文章

  • 邮局--dp经典问题
    题目:http://poj.org/problem?id=1160题意: 一些村庄被建立在一条笔直的高速公路边上,我们用一条坐标轴来描述这条高速公路,每一个村庄的坐标都是整数,没有两个村庄坐标相同。两个村庄间的距离,定义为它们的坐标值差的绝对值。我们需要在一些村庄建立邮局——当然,并不是每一个村庄都......
  • 棋子--状态压缩dp
    题目描述:在一个N*N的棋盘上放棋子,每一个棋子的上下左右都没有棋子,也就是不相邻,一共有多少种放法?(N<=8)sample_input013sample_output1263分析:首先,我们可以逐行来摆放这些棋子,这样我们会发现,每一行的摆放状态只和上一行有关,这样我们可以采用递推的方式来解决。假设f[i......
  • 深度理解链式前向星
    我们首先来看一下什么是前向星.前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了.用len[i]来记录所有以i为起点的边在数组中的......
  • 当鼠标滑过文本框自动选中输入框内容JS代码
    代码:<html><head><title>响应鼠标自动选中文本框内容</title></head><body><inputid="a"type="text"value="请输入搜索词"οnmοuseοver="selectInputContent(this.id)"/><scripttype="text/......
  • 二进制位交换,反转,与统计1的个数
    问题一:给一个整数v,求它的二进制表示中从右往左数第x位和第y位交换后的值(从0开始计数)。 分析:举个例子,如果v的二进制表示为XXXXaXXXXXXbX,我们交换第1位和第8位。我们是这样做的:先把这两位的值都取零后的值保存在一个变量里面,即tmp=XXXX0XXXXXX0X,然后再取ans=0000b000000a0,那么......
  • 求树的重心
    题目:http://poj.org/problem?id=1655题意:给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果size相同就选取编号最小的.分析:首先要知道什么是树的重心,树的重心定义为:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删......
  • DP之花店橱窗布置
    题目:https://www.smartoj.com/p/1286分析:花瓶是有序的,花也是有序的,这就保证了有序性,从而满足子解的全局最优,和无后效性.假设dp[i][j]表示前i朵花,放在前j个花瓶里的最优值.则有: 那么经过优化后得到:#include<iostream>#include<string.h>#include<stdio.h>usingnamespac......
  • SBT模版(Size Balanced Tree)
    关于SBT的介绍及学习,请戳这里。 SBT模版:/*************************************************数据结构:SBT(SizeBalancedTree),又称傻逼树;数据域:值域key,左孩子left,右孩子right,保持平衡的size;性质:每棵子树的大小不小于其兄弟的子树大小;插入:插入算法先简单插入节......
  • map()和zip()操作
    对于map()它的原型是:map(function,sequence),就是对序列sequence中每个元素都执行函数function操作。比如之前的a,b,c=map(int,raw_input().split()),意思就是说把输入的a,b,c转化为整数。再比如:a=['1','2','3','4']printmap(list,a)printmap(int,a)第一个map是把列表a中每个......
  • input()与raw_input()
    首先,我们知道input()和raw_input()都是用来获取控制台的输入,当然输入的时候可以加上输入提示信息:    a=raw_input("Pleaseinputa:")    b=input("Pleaseinputb:")那么这两者有什么区别呢?input()支持用户输入数字或者表达式,不支持输入字符串,返回的......