题意:有一个国王要把他的领土分给两个儿子,国王的领土是一棵树,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; }