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;
}
你只有十分努力,才能看上去毫不费力。
原文地址:https://www.cnblogs.com/214txdy/p/14024352.html