题解 P2458 【[SDOI2006]保安站岗】
作者:互联网
Solution [SDOI2006]保安站岗
题目大意:给定一棵\(n\)个点的树,每个点可以覆盖与之相连的所有点,求最小点集覆盖所有点。
我们用\(f[u][d]\)表示以\(u\)为根的这棵子树,\(u\)点的覆盖状态为\(d\)时的最小点花费.(\(d = 0\)时\(u\)点被父节点覆盖,\(d = 1\)时\(u\)点被自己覆盖,\(d = 2\)时\(u\)点被子节点覆盖)
那么
\[f[u][0] = \sum_{v \in son(u)} min(f[v][1],f[v][2])\]
\(f[u][1] = \sum_{v \in son(u)} min(f[v][0],f[v][1],f[v][2]) + val(x)\)
\(f[u][2] = f[x][1] + \sum_{v \in son(u),v \neq x} min(f[v][1],f[v][2])\)
关键在于\(f[u][2]\)的处理,\(n^2\)枚举时间爆炸
我们可以预处理一个\(sum = \sum min(f[v][1],f[v][2])\)
然后\(f[u][2] = min\{sum - min(f[v][1],f[v][2]) + f[v][1]\}\)
代码:
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 2048;
vector<int> G[maxn];
template <typename T>
inline T min(const T &a,const T &b,const T &c){
return min(a,min(b,c));
}
inline void addedge(int from,int to){
G[from].push_back(to);
}
int f[maxn][3],val[maxn];
void dfs(int u,int faz){
int sum = 0;
f[u][1] = val[u];
f[u][2] = 0x7fffffff;
for(int v : G[u])
if(v != faz){
dfs(v,u);
f[u][0] += min(f[v][1],f[v][2]);
f[u][1] += min(f[v][0],f[v][1],f[v][2]);
sum += min(f[v][1],f[v][2]);
}
for(int v : G[u]){
if(v == faz)continue;
f[u][2] = min(f[u][2],sum - min(f[v][1],f[v][2]) + f[v][1]);
}
}
int n,u,m,v;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d",&u),scanf("%d %d",val + u,&m);
while(m--)scanf("%d",&v),addedge(u,v),addedge(v,u);
}
return dfs(1,0),printf("%d\n",min(f[1][1],f[1][2])),0;
}
标签:const,覆盖,min,int,题解,sum,P2458,SDOI2006,maxn 来源: https://www.cnblogs.com/colazcy/p/11515083.html