zoukankan      html  css  js  c++  java
  • 皇宫看守


    Description:

    太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。 
    皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。 
    可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。 

    Input:

    帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

    Output:

    输入文件中数据表示一棵树,描述如下: 
    第1行 n,表示树中结点的数目。 
    第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<i<=n),在该宫殿安置侍卫所需的经费k,该边的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号r1,r2,...,rm。
    对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。 


    这道题中的数据是一棵树,很容易想到树形DP。//树的最小带权覆盖集

    乍一看此题和战略游戏十分接近(所以我一开始就按战略游戏的代码写),但是往深处想他们之间还是有不同的。这道题要求最小花费,而战略游戏求解最少安放士兵的数目,所以在转移上有一点(很大)不同。

    solution:

    此题没有明确的说明根节点,所以在读入时要做一下处理,找到没有子结点的点当成根结点(下文中用root代替),方便进行树上递归。

    转移:

    前面也说过,树上的DP转移一般有两个方向:从根到叶和从叶到根。此题满足从叶到根。

    对于每一个结点,只要在其本身或者父结点或者子结点有侍卫时,就一定可以被观察到,所以转移的状态可以简单的分为这三种。(son[x]表示x的子结点)

      f[x][0]表示x没有侍卫,但x的父结点有侍卫;

      f[x][1]表示x没有侍卫,但x的子结点有侍卫;

      f[x][2]表示x本身有侍卫。

    对于f[x][0]的情况:

      此时x结点已经被观察到,因为想要保证花费最小,所以此时x结点不必要安放侍卫即可。

      那么对于x的所有子结点只有两种情况:自己有侍卫或者可以被后代有侍卫。

      状态转移方程是f[x][0]+=min(f[son[x]][2],f[son][x]][1])

      (先不要纠结d是什么意思,等下我会解释) 

    对于f[x][1]的情况:

      此时x结点已经被观察到,所以x结点不必安放侍卫。

      只用找出x的所有子结点中花费最小的即可。

      同样的,子结点也只有上述的两种情况。

      状态转移方程是f[x][1]+=min(f[son[x]][2],f[son[x]][1])+d

      这个充满神秘感的d又一次出现了,现在我要揭开它神秘的面纱:

      d=min(d,f[son[x]][2]-min(f[son[x]][2],f[son[x]][1]))

      因为刚才所说的都是对结点x进行决策的,对于f[x][0]f[x][1],如果最优解是从f[son][x]][1]转移来的,那么必然也要加上它的花费。

      所以d的作用就是判断是否是从f[son][x]][1]转移来,如果是就加上它的花费。(具体使用参考以下程序)

    对于f[x][2]的情况:

      此时x结点已经有侍卫了,x的儿子可以有侍卫,也可以被其子结点看守,还可以被其父结点看守。

      所以状态转移方程是min(f[son[x]][0],f[son[x]][1],f[son[x]][2])+w[x]

      (w[x]表示在x结点安放侍卫的花费)

    出口:

    最后对f[root][1]f[root][2]取最小就是最终答案

    具体转移就是这样,下面放代码咯:)

    #include<bits/stdc++.h>
    using namespace std;
    vector<int> s[1505];
    int w[1505];
    int f[1505][3];
    bool v[1505];
    void dp(int u){
    //    f[u][2]//self
    //    f[u][1]//son
    //    f[u][0]//fa
        int y;
        int d=0x7fffffff/2;
        int sz=s[u].size();
        for(int i=0;i<sz;i++){
            dp(s[u][i]);
            f[u][0]+=min(f[s[u][i]][2],f[s[u][i]][1]);
            f[u][1]+=min(f[s[u][i]][2],f[s[u][i]][1]);
            d=min(d,f[s[u][i]][2]-min(f[s[u][i]][2],f[s[u][i]][1]));
            f[u][2]+=min(f[s[u][i]][2],min(f[s[u][i]][1],f[s[u][i]][0]));
        }
        f[u][1]+=d;
        f[u][2]+=w[u];
    }
    int main(){
        int n;
        cin>>n;
        int num,k,r,m;
        for(int i=1;i<=n;i++){
            cin>>num>>k>>m;
            w[num]=k;
            for(int j=1;j<=m;j++){
                cin>>r;
                v[r]=1;
                s[num].push_back(r);
            }
        }
        int root;
        for(int i=1;i<=n;i++){
            if(v[i]==0){
                root=i;
                break;
            }
        }
        dp(root);
        cout<<min(f[root][1],f[root][2]);
        return 0;
    } 
    皇宫看守

     代码中我用的是vector来实现链表,当然也可以用结构体来实现。

    就这么多啦:)欢迎指正!!

  • 相关阅读:
    7. 流程控制
    6. 工作区域与包
    5. Go函数
    4. Go常量
    面试题1
    数据库三范式
    触发器和存储过程
    面试题
    js 程序执行与顺序实现详解 ,来自网上
    基础的优化数据库查询,个人笔记
  • 原文地址:https://www.cnblogs.com/duojiaming/p/11220769.html
Copyright © 2011-2022 走看看