POJ 3710 Christmas Game#经典图SG博弈

http://poj.org/problem?id=3710

(说实话对于Tarjan算法在搞图论的时候就没搞太懂,以后得找时间深入了解)

(以下有关无向图删边游戏的资料来自论文贾志豪《组合游戏略述——浅谈SG游戏的若干拓展及变形》)

首先,对于无向图的删边游戏有如下定理性质:

1.(Fushion Principle定理)我们可对无向图做如下改动:将图中的任意一个偶环缩成一个新点,任意一个奇环缩成一个新点加一个新边;所有连到原先环上的边全部改为与新点相连;这样的改动不影响图的SG值。

2.(1)对于长度为奇数的环,去掉其中任意一个边之后,剩下的两个链长度同奇偶,抑或之后的SG值不可能为奇数,所以它的SG值为1;

    (2)对于长度为偶数的环,去掉其中任意一个边之后,剩下的两个链长度异奇偶,抑或之后的SG值不可能为0,所以它的SG值为0;

3.对于树的删边游戏,有如下定理:

          叶子节点的SG值为0;中间节点的SG值为它的所有子节点的SG值+1后的异或和。

所以对于这道题,用连通图的Tarjan算法找出环,然后删环,变成简单树,再进行Nim计算即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

vector<int> edge[105]; //邻接表
int belong[105][105]; //存放边的数量
int low[105],dfn[105];
int s[105],top; //堆栈
bool instack[105];
bool vis[105]; //用于标记不需要的点

void tarjan(int u,int pre,int depth)
{
    low[u]=dfn[u]=depth;//depth是时间戳,即level
    s[top++]=u;
    instack[u]=true;
    for(int i=0;i<edge[u].size();i++)
    {
        int v=edge[u][i];
        if(v==pre&&belong[u][v]>1) //判断重边
        {
            if(belong[u][v]%2==0)//偶环
                vis[u]=true;
            continue;
        }
        if(!dfn[v])
        {
            tarjan(v,u,depth+1);
            low[u]=min(low[u],low[v]);
        }
        else if(v!=pre&&instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        int cnt=1;
        top--;
        while(s[top]!=u)
        {
            vis[s[top--]]=true;
            cnt++;
        }
        if(cnt&&(cnt&1)) //若节点为奇数,则保留两个点加一条边
            vis[s[top+1]]=false;
    }
}

int getsg(int u,int pre)
{
    int res=0;
    for(int i=0;i<edge[u].size();i++)
    {
        int v=edge[u][i];
            res^=(getsg(v,u)+1);
        //叶子节点sg=0,其所有子节点的sg+1后进行异或
    }
    return res;
}

void init(int m)
{
    for(int i=1;i<=m;i++)
        edge[i].clear();
    memset(belong,0,sizeof(belong));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    memset(vis,0,sizeof(vis));
    top=0;
}

void add_edge(int u,int v)
{
    belong[u][v]++;
    belong[v][u]++;
    edge[u].push_back(v);
    edge[v].push_back(u);
}

int main()
{
    int n,m,k;
    while(~scanf("%d",&n))
    {
        int res=0;
        while(n--)
        {
            scanf("%d%d",&m,&k);
            init(m);

            while(k--)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                add_edge(u,v);
            }
            tarjan(1,-1,1);
            res^=getsg(1,-1);
        }
        if(res)
            printf("Sally\n");
        else printf("Harry\n");
    }
    return 0;
}
时间: 2024-10-11 12:11:36

POJ 3710 Christmas Game#经典图SG博弈的相关文章

poj 3710 Christmas Game(树上的删边游戏)

Christmas Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1967   Accepted: 613 Description Harry and Sally were playing games at Christmas Eve. They drew some Christmas trees on a paper: Then they took turns to cut a branch of a tre

POJ 3710 Christmas Game

Harry and Sally were playing games at Christmas Eve. They drew some Christmas trees on a paper: Then they took turns to cut a branch of a tree, and removed the part of the tree which had already not connected with the root. A step shows as follows: S

HDU 1848(sg博弈) Fibonacci again and again

Fibonacci again and again Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 6253    Accepted Submission(s): 2603 Problem Description 任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的: F(1)=1; F(2)

经典中的博弈:第一章 C++的Hello,World!

经典中的博弈:第一章 C++的Hello,World! 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! "程序设计要通过编写程序的实践来学习"-Brian Kernighan 1.1 程序 何为程序?简单的说,就是为了使计算机能够做事,你需要在繁琐的细节中告诉它怎么做.对于怎么做的描述就是程序.编程是书写和测试怎么做的过程.维基百科上说,一个程序就像一个用汉语(程序设计语言)写下的红烧肉菜谱(程序),用于指

POJ 3710

树的删边游戏.. 由于题目的特殊性,我们只需计算环的边数值.若为偶环,则直接把环的根节点置0.若为奇环,则留下一条边与根结点相连,并那它们的SG置0: 注意的是,两个点也可构成环,因为允许重边.所以,我们只需求点双连通分量,并判断分量中边的数量即可.然后DFS求树的SG值. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 6 using namespace std; 7 8 const

POJ 1149 网络流 合并建图

这个题目我敲了一个简单的EK,这不是难点 难点在于建图,按题目的要求 每个猪圈和顾客都建点的话,那也太多了...我看了Edelweiss里面的缩点方法才建好的图,哎,惭愧啊 实际那些猪圈根本不需要单独建点,猪圈无非就是向顾客输送流量 以及向同时开着的猪圈输送流量,这一步可以直接缩为,当某个猪圈被第一次打开,它里面的流量就全部输送给那个顾客那个点,而且可以叠加,因为每一次猪圈是可以互通的,而且猪圈本身是没有容量限制,如果有限制,那就还得再考虑. 此外,每次对猪圈的接下来的访问者都进行建边.用来输送

POJ 3258 River Hopscotch 经典二分

点击打开链接 River Hopscotch Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6189   Accepted: 2683 Description Every year the cows hold an event featuring a peculiar version of hopscotch that involves carefully jumping from rock to rock in a r

POJ 2226 缩点建图+二分图最大匹配

这个最小覆盖但不同于 POJ 3041,只有横或者竖方向连通的点能用一块板子覆盖,非连续的,就要用多块 所以用类似并查集方法,分别横向与竖向缩点,有交集的地方就连通,再走一遍最大匹配即可 一开始还有点没想清楚缩点怎么写,其实就是横向和竖向分别缩一下,不要混在一起,否则很麻烦,要注意一下 #include <iostream> #include <cstdio> #include <cstring> using namespace std; char mat[900][9

POJ 1637 Sightseeing tour(混合图的欧拉回路)

题目链接 建个图,套个模板. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <algorithm> #include <vector> #include <string> #include <queue> using namespace std; #define INF 0x3ffffff str