hdu--6105--Gameia

来源:互联网 发布:员工培训软件 编辑:程序博客网 时间:2024/05/16 01:39

Gameia

题目描述

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?

输入

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

输出

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

样例输入

22 113 11 2

样例输出

BobAlice

题意:

 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,这一种方法则是根据子树的节点数直接进行判断,若以题目给出树的任意节点为根的树,他的各个子树的节点数,为奇数的个数大于等于二,则这棵树就不能被分割成若干个点对。因为如果子树的节点数为奇数个,要想这棵子树的节点全部进行两两配对,就要用到根节点,而根节点只有一个,所以当有两个及其以上个,就不能实现全部配对。

第一种判断方法的代码:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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, 0sizeof(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");
    }
}

第二种判断方法的代码:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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");
    }
}




原创粉丝点击