HDU-Gameia

Gameia

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 878 Accepted Submission(s): 211

Problem Description

Alice and Bob are playing a game called ‘Gameia ? Gameia !’. The game goes like this :
0. There is a tree with all node unpainted initial.
1. Because Bob is the VIP player, so Bob has K chances to make a small change on the tree any time during the game if he wants, whether before or after Alice’s action. These chances can be used together or separate, changes will happen in a flash. each change is defined as cut an edge on the tree.
2. Then the game starts, Alice and Bob take turns to paint an unpainted node, Alice go first, and then Bob.
3. In Alice’s move, she can paint an unpainted node into white color.
4. In Bob’s move, he can paint an unpainted node into black color, and what’s more, all the other nodes which connects with the node directly will be painted or repainted into black color too, even if they are white color before.
5. When anybody can’t make a move, the game stop, with all nodes painted of course. If they can find a node with white color, Alice win the game, otherwise Bob.
Given the tree initial, who will win the game if both players play optimally?

Input

The first line of the input gives the number of test cases T; T test cases follow.
Each case begins with one line with two integers N and K : the size of the tree and the max small changes that Bob can make.
The next line gives the information of the tree, nodes are marked from 1 to N, node 1 is the root, so the line contains N-1 numbers, the i-th of them give the farther node of the node i+1.

Limits
T≤100
1≤N≤500
0≤K≤500
1≤Pi≤i

Output

For each test case output one line denotes the answer.
If Alice can win, output “Alice” , otherwise “Bob”.

Sample Input

2
2 1
1
3 1
1 2

Sample Output

Bob
Alice

题意:

Alice和Bob在玩游戏,开始有一棵树,没有涂任何颜色,这两个人轮流给树上的节点涂颜色,Alice涂白色,Alice先,Bob涂黑色,Bob是vip玩家,有如下特权:
1,当Bob把某个节点涂黑时,可以把和这个节点相邻的节点颜色变为黑色,
2,Bob有k次机会可以在任意的时候删除一条边
当游戏进行到结束时,如果这棵树上还有白色的节点,则Alice赢,题目给出树的节点的个数,(除根节点)各个节点的父节点,以及Bob可以使用删除边的特权的次数k。

解题思路:
一,Bob要想赢,就必须要把这棵树分解成若干两个点一组的点对,所以Bob赢的第一个条件为树的节点数必须为偶 数个。
二,Bob要想把树分解为若干个点对,就需要用到删除边的这个特权,所以他使用这种特权的次数k就需要满足K> (n/2-1)。这是Bob想赢的第二个条件。
三,当前面的两个条件都满足时,就要看这棵树的结构,能不能被分割成若干个点对,这里有两种判断的方法.
具体如下:
1,这种方法是模拟涂色的过程,应该是从叶节点上往上匹配,所以父节点必须在孩子节点匹配以后再匹配(拓扑排序),没有匹配的节点都应该和他的父亲匹配,那么只需要判断他(他自己没有染色,并且他的儿子都已经匹配过)的父亲是否被染色了即可,若未染色,则将他和他的父亲都染上色,否则他没法完成配对,则一定是白棋赢。
2,这一种方法则是根据子树的节点数直接进行判断,若以题目给出树的任意节点为根的树,他的各个子树的节点数,为奇数的个数大于等于二,则这棵树就不能被分割成若干个点对。因为如果子树的节点数为奇数个,要想这棵子树的节点全部进行两两配对,就要用到根节点,而根节点只有一个,所以当有两个及其以上个,就不能实现全部配对。

第一种方法:

#include<bits/stdc++.h>
using namespace std;
vector<int >vt[505];
int a[505];//a[i]=x表示节点i的父节点是x
int b[505];//染色标记
int topo[505];//排序后的节点顺序
int cnt;
void toposort(int d)
{
    ///拓扑排序
    for(int i = 0; i < vt[d].size(); i++)
        toposort(vt[d][i]);//先将d节点的儿子排序
    topo[cnt++] = d; //然后再放节点d
    return ;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        for(int i = 0; i < 505; i++)vt[i].clear();
        memset(b, 0, sizeof(b));
        cnt = 0;
        b[0] = 1;
        a[1] = 0; //因为1节点没有父节点,为了避免特判,所以给1添个已经染色的父节点0;不影响结果。
        int n, k;
        scanf("%d%d", &n, &k);
        for(int i = 2; i <= n; i++)
        {
            scanf("%d", &a[i]);
            vt[a[i]].push_back(i);
        }
        int flag = 0;
        if(n % 2)  flag = 1; //节点数为奇数一定是白赢
        if(!flag)
        {
            toposort(1);
            for(int i = 0; i < n; i++)
                if(!b[topo[i]])
                {
                    if(b[a[topo[i]]])
                    {
                        ///如果子节点没有被染色,父节点已经染色,则这个节点就无法进行配对,
                        flag = 1;
                        break;
                    }
                    else
                    {
                        b[a[topo[i]]] = 1;
                        b[topo[i]] = 1;
                    }
                }
        }
        if(n / 2 - 1 > k)
            flag = 1; //判断k次够不够
        if(flag)
            printf("Alice\n");
        else  printf("Bob\n");
    }
}

第二种方法:

#include<bits/stdc++.h>
using namespace std;
vector<int>vt[505];
int size[505];
int flag = 0;

void dfs(int u)
{
    int num = 0;
    size[u] = 1;
    for(int i = 0; i < vt[u].size(); i++)
    {
        int to = vt[u][i];
        dfs(to);
        size[u] += size[to];
        if(size[to] % 2 == 1)
            num++;
    }
    ///如果这棵树的各个子树的节点数,为奇数的个数大于等于2,则这棵数就不符合条件
    ///因为如果子树的节点数奇数,就必须要用到根节点,才能全部进行配对,而根节点只有一个
    if(num >= 2)
        flag = 1;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        for(int i = 0; i < 505; i++)
            vt[i].clear();
        int n, k;
        scanf("%d%d", &n, &k);
        for(int i = 2; i <= n; i++)
        {
            int f;
            scanf("%d", &f);
            vt[f].push_back(i);
        }
        flag = 0;
        dfs(1);
        if(flag == 1 || n % 2 == 1)
            printf("Alice\n");
        else if(n / 2 - 1 > k)
            printf("Alice\n");
        else
            printf("Bob\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值