HDU 6105 Gameia(博弈论+树形DP)

58 篇文章 1 订阅
38 篇文章 1 订阅

Description

一棵树,初始时每个节点都没有被染色, Bob K 次机会在任意时刻一起或单独使用,每次可以切掉一条树边,游戏开始,Alice Bob 轮流对树染色, Alice 先来,当 Alice 染色时,她可以选择任意一个没有染色的点将其染成白色,当 Bob 染色时,他可以选择任意一个没有染色的点将其染成黑色,并将与这个点有边相连的点全部变成白色,让某个人无点可染时游戏终止,如果此时有白点则 Alice 获胜,否则 Bob 获胜,如果两人足够机智问谁赢

Input

第一行一个整数 T 表示用例组数,每组用例首先输入两个整数N,K表示树的点数和 Bob 删边的机会,之后输入 N1 个整数,第 i 个值表示i+1节点的父亲节点 Pi (T100,1N500,0K500,1Pi<i)

Output

输出胜者

Sample Input

2
2 1
1
3 1
1 2

Sample Output

Bob
Alice

Solution

如果 Bob 可以把这棵树分成两两匹配的点对则显然 Bob 获胜,下证其他情况均为 Alice 获胜

一.如果不存在这种两两匹配的方案,那么 Alice 每次从叶子节点开始染叶子节点的父亲节点,那么 Bob 就只能染叶子节点,否则下一步 Alice 染叶子 Bob 无法将该叶子节点变成黑色, Bob 的操作失去意义,因为就算 Bob 删边,由于不存在两两匹配的方案,最终必然是 Bob 用完删边的机会被 Alice 染到一个周围都被染色的孤立点进而获胜

二.如果存在两两匹配的方案但是 Bob 的操作不能把这棵树分成两两匹配的点对,每次 Alice 染一个没有兄弟的叶子节点,那么 Bob 就必须染这个点的父亲节点,但是由于染父亲节点会让父亲节点的父亲节点也变成黑色,没染色的节点数变成奇数,由第一种情况知 Bob 失败,故 Bob 被迫把父亲节点与其父亲节点之间的边删掉使得该叶子节点与其父亲节点孤立出来形成一个点对,重复此过程最终会有两种情况:

1.所有叶子节点均有兄弟节点, Alice 染这个叶子节点的父亲节点,那么 Bob 为使该点变成黑色就会染该点的父亲节点或该点的儿子节点,但是总会多出孤立的叶子节点, Alice 获胜

2. Bob 删边次数用光, Bob 被迫染色形成一个奇数节点的子树,由第一种情况知 Alice 获胜

综上,只有当这棵树可以被分成两两匹配的点对且 Kn21 Bob 获胜,判断该棵树是否可以分成两两匹配的点对只需要从叶子节点贪心的匹配即可,一遍树形DP即可解决

Code

#include<cstdio>
#include<algorithm>
using namespace std;
namespace fastIO 
{
    #define BUF_SIZE 1000
    //fread -> read
    bool IOerror=0;
    inline char nc() 
    {
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if(p1==pend) 
        {
            p1=buf;
            pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if(pend==p1) 
            {
                IOerror=1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) 
    {
        return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
    }
    inline void read(int &x) 
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
    }
    inline void readc(char &x)
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        x=ch;
    }
    #undef BUF_SIZE
};
using namespace fastIO;
const int maxn=501;
struct node
{
    int v,next;
}e[maxn];
int head[maxn],tot;
int T,n,k,fa[maxn];
void add(int u,int v)
{
    e[tot].v=v,e[tot].next=head[u],head[u]=tot++;
}
int dfs(int u)
{
    int num=0;
    for(int i=head[u];~i;i=e[i].next)
    {
        int t=dfs(e[i].v);
        if(t==-1)return -1;
        if(t==1)num++;
        if(num>1)return -1;//有多个还没匹配的儿子则Alice赢 
    } 
    if(num==0)return 1;//所有儿子全部匹配,该点未匹配 
    return 0;//该点与某个儿子匹配 
}
int main()
{
    read(T);
    while(T--)
    {
        read(n),read(k);
        for(int i=2;i<=n;i++)read(fa[i]);
        int flag=1;
        if(n%2==0&&k>=n/2-1)
        {
            tot=0;
            for(int i=1;i<=n;i++)head[i]=-1;
            for(int i=2;i<=n;i++)add(fa[i],i);
            if(dfs(1)!=-1)flag=0;
        }
        printf("%s\n",flag?"Alice":"Bob");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值