zoukankan      html  css  js  c++  java
  • Vijos 1144 小胖守皇宫 【树形DP】

    题目:

    皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。

    可是小胖手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。

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

    分析:

    此题一看就能找出树形DP的影子,它的图都是以树的形式呈现的,而所谓的“可以相互望见”,其实就是子节点和父节点之间的关系,那么我们就可以想到用子节点通过递推得到父节点。这个题与“没有上司的舞会”那道题非常相像:

    http://gxyz.openjudge.cn/20170402/03/

    当我们分析一个节点的时候,很容易想到起码有两种情况:在这个节点设侍卫或者不设。但是如果单纯地只是分析它不设,那么依据题意,它必须得被观测到,那么它的子节点到底要不要设呢?此时便又要分两种情况:被儿子观测或是被父亲观测。因此到这里我们把这个题已经抽象成了三种状态转移:

    f [ i ] [ 1 ]表示在i节点的子节点设一个侍卫的最小经费(默认i节点自己不设)

    f [ i ] [ 2 ]表示在i节点设一个侍卫的最小经费

    f [ i ] [ 3 ]表示在i节点的父节点设一个侍卫的最小经费(默认i节点自己不设)

    1、f [ i ] [ 1 ] 表示i节点被它的子节点观测到,这时我们就要考虑:是不是子节点一定会再自己那里设一个观测点呢?当然不一定了,当且仅当子节点 f [ son[i] ] [ 2 ] < f [ son[i] ] [ 1 ] 时,他才会在自己那里设。那么我们就需要找遍i的所有儿子,只要有一个儿子可以设就行了。这个怎么实现呢?我们只需先把所有儿子的min( f [ v ] [ 1 ] , f [ v ] [ 2 ] )累加到f [ i ] [ 1 ]上,再取所有儿子的 f [ v ] [ 2 ] - min( f [ v ] [ 1 ] , f [ v ] [ 2 ] )最小值 t,如果这个最小值 t不是0,就说明没有一个儿子打算在自己那里设点,就必须强迫一个儿子改变最优解,来满足i节点的需求,即用f [ i ] [ 2 ]加上t ,若t 是0,不影响,加上也无妨。

    f [ i ] [ 1 ] += min( f [ v ] [ 1 ] , f [ v ] [ 2 ] );//既然默认自己不会设点,就不可能有 f [ v ] [ 3 ]这个状态

    f [ i ] [ 1 ] += t;

    2、f [ i ] [ 2 ] 表示i节点自己那里设了一个侍卫,既然自己已经设了一个,那么子节点的三种情况都要考虑。

    f [ i ] [ 2 ] += min( f [ v ] [ 1 ] , f [ v ] [ 2 ] , f [ v ] [ 3 ] );

    3、f [ i ] [ 3 ] 表示i节点被它的父节点观测到,那么除了f [ v ] [ 3 ]要被排除掉,其他的只要保证子节点可以被观测到即可。

    f [ i ] [ 3 ] += min( f [ v ] [ 1 ] , f [ v ] [ 2 ] );

    下面是参考代码:

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    using namespace std;
    vector<int> pal[1550];
    int f[1550][4],nson[1550],p[1550]={0},cost[1550];
    int n; 
    void tree_dp(int);
    int main()
    {
     cin>>n;
     for(int i=1;i<=n;i++)
     {
      int id;
      cin>>id;
      cin>>cost[id]>>nson[id];
      for(int j=1;j<=nson[id];j++)
      {
       int x;
       cin>>x;
       p[x]++;
       pal[id].push_back(x); 
      }
     }
     for(int i=1;i<=n;i++)
      if(p[i]==0)
      {
       tree_dp(i);
       cout<<min(f[i][1],f[i][2]);   
      }
     return 0;
    }
    void tree_dp(int root)
    {
     int t=0x3f3f3f3f;
     for(int i=0;i<nson[root];i++)
     {
      //1==>by son 2==>by itself 3==>by father
      int son=pal[root][i];
      tree_dp(son); 
      f[root][1]+=min(f[son][1],f[son][2]);
      t=min(t,f[son][2]-min(f[son][1],f[son][2]));
      f[root][2]+=min(f[son][1],min(f[son][2],f[son][3]));
      f[root][3]+=min(f[son][1],f[son][2]);
     }
     f[root][1]+=t;
     f[root][2]+=cost[root]; 
     return;
    }
    View Code
  • 相关阅读:
    redis 3 通用指令
    查看表索引
    truncate的用法
    Java(0)_ 安装jdk
    Java(9)_ 集合(3)
    Java(10)_File&递归&字节流
    Java(8)_ 集合(2)
    Appium+python的单元测试框架unittest(3)——discover
    Appium+python的单元测试框架unittest(2)——fixtures
    爬楼梯
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/7206257.html
Copyright © 2011-2022 走看看