• poj3710 (无向图删边博弈)


    引入:树上删边博弈

    例题:给出一个有 N个点的树,有一个点作为树的根节点。游戏者轮流从树中删去边,删去一条边后,不与根节点相连的部分将被移走。谁无法移动谁输。

    结论:叶子节点的SG值为0;中间节点的SG值为它的所有子节点的SG值加1后的异或和。根节点SG值非0先手胜。

    无向图的删边博弈

    例题(poj 3710): 一个无向联通图,有一个点作为图的根,一条边最多涉及一个环,每个环只有一个结点与树相连。游戏者轮流从图中删去边,删去一条边后,不与根节点相连的部分将被移走。谁无法继续操作谁输。

    输入格式:多组输入。每个测试用例的第一行是一个整数N(N<100),它表示子树的数目。第一行是节点数m(m<100)和边数k(k<500)。树的节点从1到m编号。以下每行包含2个整数a和b,表示一条边<a,b>。节点1始终是根。

    输出格式:对于每组测试数据,输出胜者姓名。

    结论:将图中的任意一个偶环缩成一个新点,任意一个奇环缩成一个新点加一个新边;所有连到原先环上的边全部改为与新点相连。这样的改动不会影响图的SG 值。然后就是树上删边博弈。

    #include<cstdio>
    #include<cstring>
    #define min(a,b) (a<b ? a:b)
    
    int cnt,n,m,k,x,y,top,sg,tot;
    int head[105],dfn[105],low[105],sta[105],kk[105][105];
    bool vis[105],in[105];
    
    struct Node{
        int nex,to;
    }e[1005];
    
    inline void add(int a,int b){
        ++cnt;
        e[cnt].nex = head[a];
        e[cnt].to = b;
        head[a] = cnt;
    }
    
    void init(){
        cnt = tot = top = 0;
        memset(dfn,0,sizeof(dfn));
        memset(vis,0,sizeof(vis));
        memset(head,0,sizeof(head));
        memset(kk,0,sizeof(kk));
    }
    
    void tarjan(int u,int f){
        sta[++top] = u, in[u] = 1;
        low[u] = dfn[u] = ++tot;
        for(int i = head[u]; i; i = e[i].nex){
            int to = e[i].to;
            if(to == f && kk[to][u]>1){
                if(kk[to][u]%2 == 0)    vis[u] = 1;   
                continue;
            }
            if(!dfn[to]){
                tarjan(to,u);
                low[u] = min(low[u],low[to]);
            }
            else if(to != f && in[to])  low[u] = min(low[u],dfn[to]);
        }
        if(dfn[u] == low[u]){
            int num = 1;
            while(sta[top] != u){
                ++num, in[sta[top]] = 0;
                vis[sta[top--]] = 1;
            }
            --top, in[u] = 0;
            if((num&1) && num > 1)   vis[sta[top+2]] = 0;
        }
    }
    
    int get_sg(int u,int f){
        int sg = 0;
        for(int i = head[u]; i; i = e[i].nex){
            int to = e[i].to;
            if(to != f && !vis[to])  sg ^= (get_sg(to,u)+1);
        }
        return sg;
    }
    
    int main(){
        while(scanf("%d",&n) != EOF){
            sg = 0;
            while(n--){
                scanf("%d%d",&m,&k);  init();
                for(int i = 1; i <= k; ++i){
                    scanf("%d%d",&x,&y);
                    add(x,y), add(y,x);
                    ++kk[x][y], ++kk[y][x];
                }
                tarjan(1,-1);
                sg ^= get_sg(1,-1);
            }
            if(sg)  puts("Sally");    else    puts("Harry");        
        }
        return 0;
    }
    你只有十分努力,才能看上去毫不费力。
  • 相关阅读:
    Python并行编程(十三):进程池和mpi4py模块
    Python 列表集合 字典推导式、生成器表达式
    Python 迭代对象、迭代器
    Python 参数,嵌套函数 的变量 使用
    Python 编码进阶
    Python 深浅Copy
    Python 代码块、缓存机制
    Python 列表,字典 相关方法
    初识 python 字符串 相关函数
    初识编码格式
  • 原文地址:https://www.cnblogs.com/214txdy/p/14024352.html
Copyright © 2020-2023  润新知