zoukankan      html  css  js  c++  java
  • 树上最小点覆盖问题

    树上最小点覆盖问题:消耗一个节点上的花费可以覆盖它自己及所有与它的相邻节点,求覆盖树上所有节点最小花费。

    1077. 皇宫看守

    太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
    皇宫各个宫殿的分布,呈一棵树的形状,宫殿可视为树中结点,两个宫殿之间如果存在道路直接相连,则该道路视为树中的一条边。
    已知,在一个宫殿镇守的守卫不仅能够观察到本宫殿的状况,还能观察到与该宫殿直接存在道路相连的其他宫殿的状况。
    大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
    可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
    帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
    输入格式
    输入中数据描述一棵树,描述如下:
    第一行 n,表示树中结点的数目。
    第二行至第 n+1 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i,在该宫殿安置侍卫所需的经费 k,该结点的子结点数 m,接下来 m 个数,分别是这个结点的 m 个子结点的标号 r1,r2,…,rm。
    对于一个 n 个结点的树,结点标号在 1 到 n 之间,且标号不重复。
    输出格式
    输出一个整数,表示最少的经费。
    数据范围
    1≤n≤1500
    输入样例:
    6
    1 30 3 2 3 4
    2 16 2 5 6
    3 5 0
    4 4 0
    5 11 0
    6 5 0
    输出样例:
    25
    样例解释:
    在2、3、4结点安排护卫,可以观察到全部宫殿,所需经费最少,为 16 + 5 + 4 = 25。

    每个点可以选择被儿子,自己或者父亲节点覆盖(至少被唯一次覆盖),可以用f[u][0]儿子节点防止了侍卫,f[u][1]表示自己放了侍卫,f[u][2]表示父亲放了侍卫
    f[u][0]由一个子节点的f[v][1]和其他子节点的f[v][0]和[1]的最小值(没有[2]因为不能依靠父节点),遍历一遍所有子节点的可能,取一个最小值。即只要由一个子节点上放了侍卫该节点就可以被覆盖
    f[u][1]由子节点的所有情况转移而来,因为该节点可以覆盖所有子节点
    f[u][2]由子节点的f[v][0]和f[v][1]最小值转移而来,因为它不能覆盖子节点

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1510;
    int h[N],idx,a[N],f[N][3];  //0儿子放 ,1自己放 ,2父亲放
    struct eg{
        int v,nex;
    }e[N*2];
    void add(int u,int v){
        e[idx]={v,h[u]};
        h[u]=idx++;
    }
    void dfs(int u,int pre){
        f[u][1]=a[u];
        //f[u][0]
        int res=0;
        for(int i=h[u];~i;i=e[i].nex){
            int v=e[i].v;
            if(v==pre) continue;
            dfs(v,u);
            f[u][1]+=min(f[v][0],min(f[v][1],f[v][2]));
            f[u][2]+=min(f[v][1],f[v][0]);
            res+=min(f[v][0],f[v][1]);
        }
        int ans=0x3f3f3f3f;
        for(int i=h[u];~i;i=e[i].nex){
            int v=e[i].v;
            if(f[v][0]>=f[v][1]){//儿子自己放
                ans=min(ans,res);
            }
            else { //儿子的儿子放
                ans=min(ans,res-f[v][0]+f[v][1]);
            }
            //ans=min(ans,res-min(f[v][0],f[v][1])+f[v][1]);
        }
        f[u][0]=ans;
    }
    int main(){
        int n;
        memset(h,-1,sizeof h);
        cin>>n;
        for(int i=1;i<=n;++i){
            int u,k,v;
            scanf("%d",&u);
            scanf("%d%d",&a[u],&k);
            while(k--){
                scanf("%d",&v);
                add(u,v);
                add(v,u);
            }
        }
        if(n==1) cout<<a[1]<<endl;
        else {
            dfs(1,0);
            cout<<min(f[1][0],f[1][1])<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    Codeforces Round #631 (Div. 1) B. Dreamoon Likes Sequences 题解(思维+求贡献)
    牛客练习赛66 C公因子 题解(区间gcd)
    evincevim控喜欢的pdf阅读器
    水手郑智化
    使用diskpart管理自己的分区
    Hacker's Browser
    How Browser Works
    解决vim ctags R失败的问题
    ubuntu更改登录对话框
    使用bcdedit删除多个Windows系统
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12654796.html
Copyright © 2011-2022 走看看