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
原文地址:https://www.cnblogs.com/linda-fcj/p/7206257.html