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; }