返回顶部

【洛谷】 P2458 [SDOI2006]保安站岗 (树形dp)

  • 题意:一棵\(n\)个点\(m\)条边的树,每个点都有点权,如果某个点被标记了,那么这个点和它相邻的点会被覆盖,贡献是被标记的这个点的点权,问将所有点覆盖需要花费的最小代价。

  • 题解:首先,这题不能和树形dp的模板题等同,因为假如有一颗树\(1-2-3-4\),我们把\(1,4\)标记,那么\(1,2,3,4\)就全被我们覆盖了。根据这个,我们可以的出一个点有3种覆盖情况:

    0.由自己覆盖自己,对自己标记

    1.由儿子覆盖自己,即儿子要有标记

    2.由父亲覆盖自己,即父亲要有标记

    \(dp[u][0/1/2]\)分别表示上面三种情况。

    第一种情况,因为我自己把自己覆盖了,所以随便父亲儿子怎么标记,都是合法的,\(dp[u][0]=\sum_{v\in son_u}min({dp[v][0],dp[v][1],dp[v][2]})+w[u]\).

    第三种情况,我是由父亲覆盖的,那么我自己没有标记,所以我的儿子不能由父亲覆盖,那么\(dp[u][2]=\sum _{v \in son_u}min(dp[v][0],dp[v][1]])\).

    第二种情况,我被儿子覆盖了,那么首先,儿子里面一定至少要有一个是自己标记自己的,也就是说\(dp[u][1]\)至少有一个\(dp[v][0]\)转移过来,同时我的所有儿子也一定都必须合法,\(dp[u][1]=\sum _{v \in son_u}min(dp[v][0],dp[v][1]])\).假如\(dp[u][1]\)全是由\(dp[v][1]\)转移而来,那么根据我们前面所说的至少有一个\(dp[v][0]\),就要加上一个\(min(v\in son_u|dp[v][0]-dp[v][1])\).

    叶子结点记成\(INF\)即可。

  • 代码

    #include <iostream>
    #include <iomanip>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <vector>
    #include <map>
    #include <set>
    #include <unordered_set>
    #include <unordered_map>
    #define ll long long
    #define db double
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int lcm(int a,int b){return a/gcd(a,b)*b;}
    
    int n;
    vector<int> edge[N];
    int w[N];
    ll dp[N][3];
    //0:myself 1:son 2:father
    
    void dfs(int u,int fa){
        dp[u][0]=w[u];
        bool flag=false;
        ll mi=1e18;
        for(auto to:edge[u]){
            if(to==fa) continue;
            dfs(to,u);
            dp[u][0]+=min({dp[to][0],dp[to][1],dp[to][2]});
            if(dp[to][0]<=dp[to][1]){
                flag=true;
            }
            mi=min(mi,dp[to][0]-dp[to][1]);
            dp[u][1]+=min(dp[to][1],dp[to][0]);
            dp[u][2]+=min(dp[to][0],dp[to][1]);
        }
        if(!flag) dp[u][1]+=mi;
    }
    
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            int id;
            scanf("%d",&id);
            scanf("%d",&w[id]);
            int m;
            scanf("%d",&m);
            for(int i=1;i<=m;++i){
                int v;
                scanf("%d",&v);
                edge[id].pb(v),edge[v].pb(id);
            }
        }
        dfs(1,-1);
        printf("%lld",min(dp[1][0],dp[1][1]));
        return 0;
    }
    
posted @ 2021-09-17 13:18  Rayotaku  阅读(41)  评论(0编辑  收藏  举报