[Luogu] P2899 [USACO08JAN]Cell Phone Network G
Description
John想让他的所有牛用上手机以便相互交流,他需要建立几座信号塔在\(N\)块草地中。已知与信号塔相邻的草地能收到信号。给你\(N-1\)个草地\((A,B)\)的相邻关系,问:最少需要建多少个信号塔能实现所有草地都有信号。
Solution
妙妙的树形\(DP\)。
显然\(x\)只能被自己的儿子/父亲染色后波及,或者自己就已经被染色。我们设\(dp[x][0/1/2]\)分别表示\(x\)是在\(x\)自己/自己的儿子/自己的父亲染色后被波及(被其他点波及的,自己肯定就不用染色了),且子树全被波及所需要的最少染色次数。设\(x\)的儿子为\(y\),再分别进行转移:
\(1.dp[x][0]=\sum{min(dp[y][0],dp[y][1],dp[y][2])}\)(\(y\)可以自己染色,被\(x\)波及,和被它的儿子波及)
\(2.dp[x][2]=\sum{min(dp[y][0],dp[y][1])}\)(\(y\)可以自己染色和被它的儿子波及,但不会被\(x\)波及)
而\(dp[x][1]\)的转移比较复杂。显然它也只会由\(dp[y][0]\)和\(dp[y][1]\)转移而来,但具体要怎么转移呢?
我们贪心地想,肯定是要取\(min(dp[y][0],dp[y][1])\)。但比较特殊的是,\(dp[x][1]\)也不能全由\(dp[y][1]\)转移来,因为它至少要有一个自己被染色的儿子来波及它。
所以我们先把\(dp[x][1]\)加上\(\sum{dp[y][0]}\),再把\(dp[y][1]-dp[y][0]\)的值存到一个队列\(que\)里。处理完\(x\)的所有子节点后,再来处理\(dp[x][1]\)。显然,若\(que[i]<0\),那么我们就把\(dp[y][0]\)替换成\(dp[y][1]\)(即\(dp[x][1]+=que[i]\))。所以直接从小到大排序,如果\(que[i]>0\)就\(break\)掉。这样也能保证替换的值是最优的。还要注意两点:首先如果\(x\)没有儿子,那么\(dp[x][1]\)要设成\(INF\)。其次设\(que[]\)有\(t\)个元素,我们只能枚举到\(t-1\)个元素,原因之前已经说过了。
最后要注意每次\(dfs\)要新开一个队列,不能用全局变量!!(可能是因为回溯之类的奇奇怪怪的原因吧)
Code
#include <bits/stdc++.h>
using namespace std;
int n, tot, hd[100005], to[200005], nxt[200005], dp[100005][3];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * fl;
}
void add(int x, int y)
{
tot ++ ;
to[tot] = y;
nxt[tot] = hd[x];
hd[x] = tot;
return;
}
void dfs(int x, int fa)
{
dp[x][0] = 1; int t = 0, que[100005];
for (int i = hd[x]; i; i = nxt[i])
{
int y = to[i];
if (y == fa) continue;
dfs(y, x);
dp[x][0] += min(dp[y][1], dp[y][2]);
dp[x][1] += dp[y][0];
dp[x][2] += min(dp[y][0], dp[y][1]);
que[ ++ t] = dp[y][1] - dp[y][0];
}
sort(que + 1, que + t + 1);
if (!t) dp[x][1] = 1e9;
else
{
for (int i = 1; i < t; i ++ )
{
if (que[i] < 0) dp[x][1] += que[i];
else break;
}
}
for (int i = 1; i <= t; i ++ )
que[i] = 0;
return;
}
int main()
{
n = read();
for (int i = 1; i <= n - 1; i ++ )
{
int x = read(), y = read();
add(x, y); add(y, x);
}
dfs(1, 0);
printf("%d\n", min(dp[1][0], dp[1][1]));
return 0;
}