Acdream 1015 Double Kings 搜索

题意:有一个国王要把他的领土分给两个儿子,国王的领土是一棵树,N个结点,N-1条边把这些结点连起来,现在大小儿子要选择一个点作为他的首都,那么除首都分别是这两个儿子之外,其他的城市(结点)根据离谁近就归谁所有,如果一样远的话就归大儿子所有,现在假设两个人都采取最优策略,且大儿子先选,问大儿子最多能够得到多少城市?

解法:如果大儿子选择了一个点P,那么这个小儿子选择的点一定在P点的某个子树(M)当中,且P的其他子树一定归大儿子所有,对于子树M,小儿子的最优策略显然是选择和P相邻的哪一个点,否则将损失更多的城市。那么最后的解法就是大儿子选择一定P,改点满足P的所有的子树中拥有最大结点数的子树最小。

这题前面写了一个建立在双向边的dfs,后来发现是错误的,直接一次dfs,然后根据树的特性求出另外一边子树的个数即可。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

struct Edge {
    int x, num, next;    
}e[100005];
 
int head[50005], idx, N, ans[50005];


void addedge(int x, int y) {
    
    ++idx;
    e[idx].x = y, e[idx].num = 0;
    e[idx].next = head[x];
    head[x] = idx;
}

void dfs(int x, int f) {
    for (int i = head[x]; i != -1; i = e[i].next) {
        if (e[i].x != f) { // 如果遇到父节点就跳过  
            int v = e[i].x; 
            dfs(v, x);
            for (int j = head[v]; j != -1; j = e[j].next) {
                if (e[j].x == x ){ continue; }
                e[i].num += e[j].num;
            }
            e[i].num += 1;
            e[i^1].num = N - e[i].num;
        }
    } 
}

int main() {
    int x, y, ret;
    while (scanf("%d", &N) == 1) {
        idx = -1;
        ret = 0x7fffffff;
        memset(ans, 0, sizeof (ans));
        memset(head, 0xff, sizeof (head));
        for (int i = 1; i < N; ++i) {
            scanf("%d %d", &x, &y);
            addedge(x, y);
            addedge(y, x);
        }
        dfs(1, 0);
        for (int i = 1; i <= N; ++i) {
            for (int j = head[i]; j != -1; j = e[j].next) {
                ans[i] = max(ans[i], e[j].num);
            }
            ret = min(ret, ans[i]);
        }
        printf("%d\n", N - ret);
     }
    return 0;    
}
posted @ 2012-12-01 21:36  沐阳  阅读(286)  评论(0编辑  收藏  举报