USACO08JAN 手机网络Cell Phone Network
John想让他的所有牛用上手机以便相互交流(也是醉了。。。),他需要建立几座信号塔在N块草地中。
已知与信号塔相邻的草地能收到信号。给你N-1个草地(A,B)的相邻关系,问:最少需要建多少个信号塔能实现所有草地都有信号。
(其实就是最小支配集大小)
考虑树上dp
\(f_{u,1}\)表示以u为根的子树中,u选时候的答案
\(f_{u,0}\)表示以u为根的子树中,u不选且儿子不选(也就是u必须要被父亲覆盖)时候的答案
\(f_{u,2}\)表示以u为根的子树中,u不选且至少有一个儿子选时候的答案
于是我们可以得到转移方程
\[\begin{cases}
f_{u,1}=1+\sum_{v\in son(u)}min(f_{v,0},f_{v,1},f_{v,2})& \\
f_{u,0}=\sum_{v\in son(u)}f_{v,2}& \\
f_{u,2}=f_{x,1}\ (x\in son(u))+\sum_{v\in son(u)\& v\ne x}min(f_{v,1},f_{v,2})
\end{cases}\]
u选的时候儿子是随便选的
u不选而且儿子不选那么儿子就要被儿子的儿子覆盖到
u不选但儿子选就要保证有一个点是选的,所以我们找到一个x满足\(f_{x,1}-min(f_{x,1},f_{x,2})\)最小,然后剩下的在这个点选或者不选且被儿子覆盖(因为u不选所以不能选择被父亲覆盖)里取较小的
然后注意下根是没有\(f_{1,0}\)的,叶子节点没有\(f_{u,2}\)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
const int N = 1e4;
using namespace std;
int n,f[N + 5][3];
vector <int> d[N + 5];
void dfs(int u,int fa)
{
f[u][1] = 1;
vector <int>::iterator it;
int mm = N + 5;
for (it = d[u].begin();it != d[u].end();it++)
{
int v = (*it);
if (v == fa)
continue;
dfs(v,u);
f[u][1] += min(min(f[v][0],f[v][1]),f[v][2]);
f[u][0] += f[v][2];
f[u][2] += min(f[v][1],f[v][2]);
if (mm > f[v][1] - min(f[v][1],f[v][2]))
mm = f[v][1] - min(f[v][1],f[v][2]);
}
f[u][2] += mm;
}
int main()
{
scanf("%d",&n);
int u,v;
for (int i = 1;i < n;i++)
{
scanf("%d%d",&u,&v);
d[u].push_back(v);
d[v].push_back(u);
}
dfs(1,0);
cout<<min(f[1][2],f[1][1])<<endl;
return 0;
}