UVa传送门
洛谷RemoteJudge传送门
题目大意:有n(n ≤ 10000)台机器组成树形结构,要求在其中的一些机器上安装服务器,使得每台不是服务器的计算机恰好和一台服务器计算器相连。求服务器最少的数量。
典型树形DP。
根据节点的情况进行分类。
d(u,0)
d
(
u
,
0
)
:
u
u
是服务器,子节点是或不是服务器均可。
:
u
u
不是服务器,但的父亲是服务器,这意味着
u
u
的所有儿子都不是服务器。
:
u
u
不是服务器,而且的父亲也不是服务器。这意味着
u
u
的所有儿子结点中有且仅有一个儿子是服务器。
有了刚才的解释,状态转移方程写出来也不算复杂:
d(u,1)=∑d(v,2)
d
(
u
,
1
)
=
∑
d
(
v
,
2
)
d(u,2)=d(v,0)+∑d(v′,2)
d
(
u
,
2
)
=
d
(
v
,
0
)
+
∑
d
(
v
′
,
2
)
枚举
v
v
,表示除了
v
v
以外的的子节点
由此看来,三种运算所需要的时间并不相同,
d(u,2)
d
(
u
,
2
)
需要花
O(k2)
O
(
k
2
)
的时间进行计算,时间复杂度过高,仔细观察状态其实我们可以利用已经被计算过的
d(u,1)
d
(
u
,
1
)
优化我们的状态转移方程。
d(u,1)
d
(
u
,
1
)
只需在所有
d(u,1)–d(v,2)+d(v,0)
d
(
u
,
1
)
–
d
(
v
,
2
)
+
d
(
v
,
0
)
中取个
min
m
i
n
即可。
这样子,三种状态都能在
O(k)
O
(
k
)
的时间内被计算,总的时间复杂度为
O(n)
O
(
n
)
。
Code C o d e
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define MAXN 10010
std::vector< int > G[MAXN];
int d[MAXN][3];
void solve(int u, int p) {
for (unsigned int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (v == p) continue;
solve(v, u);
d[u][0] += std::min(d[v][0], d[v][1]);
d[u][1] += d[v][2];
}
for (unsigned int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (v == p) continue;
d[u][2] = std::min(d[u][2], d[u][1] - d[v][2] + d[v][0]);
}
}
int main() {
int n;
scanf("%d", &n);
while (true) {
for (int i = 1; i <= n; i++) {
G[i].clear();
d[i][0] = 1;
d[i][1] = 0;
d[i][2] = MAXN;
}
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
solve(1, -1);
printf("%d\n", std::min(d[1][0], d[1][2]));
scanf("%d", &n);
if (n == -1) break;
scanf("%d", &n);
}
return 0;
}