题解 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;
}

猜你喜欢

转载自www.cnblogs.com/colazcy/p/11515083.html