1. 程式人生 > >樹型DP

樹型DP

概念

樹型DP即在樹上進行DP。樹是無環圖,順序可以是從葉子到根節點,也可以從根到葉子節點。一般樹型DP的特徵很明顯,即狀態可以表示為樹中的節點,每個節點的狀態可以由其子節點狀態轉移而來(從葉子到根的順序),或是由其父親節點轉移而來(從根到葉節點的順序),也可是兩者結合。找出狀態和狀態轉移方程仍然是樹型DP的關鍵。


例題

沒有上司的晚會

題目描述

Ural大學有N個職員,編號為1~N。他們有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。每個職員有一個快樂指數。現在有個週年慶宴會,要求與會職員的快樂指數最大。但是,沒有職員願和直接上司一起參加宴會。

輸入

第一行一個整數N。(1≤N≤6000)
接下來N行,第i+1行表示i號職員的快樂指數Ri。(-128≤Ri≤127)
接下來N-1行,每行輸入一對整數L,K。表示K是L的直接上司。
最後一行輸入0,0。

輸出

第1行:輸出最大的快樂指數。

樣例輸入

7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0

樣例輸出

5

分析

題意是從N個人中選出若干人,使得其快樂指數最大。但是選人有一些限制條件,即不允許上下級同時出現。
N個人的從屬構成一棵樹。設f[i][0]表示i不參加晚會,以i為根的子樹能達到的最大快樂指數;f[i][1]表示i參加晚會其所在的子樹能達到的最大的快樂指數。
則目標狀態為max(f[root][0],f[root][1])
狀態轉移方程f[i][0]=sum(max(f[j][1],f[j][0])) j為i的兒子
f[i][1]=sum([f[j][0])+happy[i] j為i的兒子。
初識狀態即葉子節點的狀態。f[leaf][0]=0,f[leaf][1]=happy[leaf]。

原始碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,x,y,root;
bool v[6001],b[6001];
int father[6001],f[6001][2];
void get(int &p){
    int flag=1;char ch;
    for(p=0;ch>'9'||ch<'0';ch=getchar())
        if(ch=='-')
            flag=-1
; for(;ch<='9'&&ch>='0';ch=getchar()) p=p*10+ch-'0'; p*=flag; } void dp(int i){ v[i]=1; for(int j=1;j<=n;j++) if(!v[j]&&father[j]==i){ dp(j); f[i][1]+=f[j][0]; f[i][0]+=max(f[j][1],f[j][0]); } } int main() { get(n); for(int i=1;i<=n;i++) get(f[i][1]); for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); father[x]=y;b[x]=1; } for(int i=1;i<n;i++) if(!b[i]){ root=i; break; } dp(root); printf("%d",max(f[root][0],f[root][1])); }

二叉蘋果樹

題目描述

有一棵蘋果樹,如果樹枝有分叉,一定是分 2 叉(就是說沒有隻有 1 個兒子的結點)。這棵樹共有 N 個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是 1。 我們用一根樹枝兩端連線的結點的編號來描述一根樹枝的位置。下面是一顆有 4 個樹枝的樹:

現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。 給定需要保留的樹枝數量,求出最多能留住多少蘋果。

輸入

第1行: 2個空格分開的整數,N 和 Q(1≤Q≤N,1<N≤100),N表示樹的結點數,Q表示要保留的樹枝數量。 接下來 N-1 行描述樹枝的資訊。 每行3 個整數,前兩個是它連線的結點的編號。第3 個數是這根樹枝上蘋果的數量。 每根樹枝上的蘋果不超過30000 個。

輸出

第1行:一個整數,表示最多能留住的蘋果的數量。

樣例輸入

5 2
1 3 1
1 4 10
2 3 20
3 5 20

樣例輸出

21

分析

將枝條上的蘋果移到靠近葉子節點的一端的節點上,更好理解。
設f[i][j]表示以i為根的子樹保留j個節點所能保留的最大蘋果數。
則f[i][j]=max(f[i.leftson][k]+f[i.rightson][j-1-k])+a[i] (0=

原始碼

#include<cstdio>
#include<iostream>
using namespace std;
struct node{int l,r,v;}tree[1001];
int flag[101],f[101][101];
int n,q,root;
void get(int &p){
    int flag=1;char ch;
    for(p=0;ch>'9'||ch<'0';ch=getchar())if(ch=='-') flag=-1;
    for(;ch<='9'&&ch>='0';ch=getchar())p=p*10+ch-'0';
    p*=flag;
}
void dfs(int i,int j){
    int k;
    if(!j)f[i][j]=0;
    else if(!tree[i].r&&!tree[i].l) f[i][j]=tree[i].v;
    else{
        f[i][j]=0;
        for(k=0;k<j;k++){
            if(!f[tree[i].l][k])dfs(tree[i].l,k);
            if(!f[tree[i].r][j-k-1])dfs(tree[i].r,j-k-1);
            f[i][j]=max(f[i][j],f[tree[i].l][k]+f[tree[i].r][j-k-1]+tree[i].v);
        }
    }
}
int main()
{
    get(n);get(q);
    for(int i=1;i<n;i++){
        bool p=0;int x,y,m;
        get(x);get(y);get(m);
        for(int j=1;j<=n;j++)
            if(tree[j].l==y||tree[j].r==y)p=1;
        if(!p){
            if(!tree[x].l)tree[x].l=y;
            else if(!tree[x].r)tree[x].r=y;
            tree[y].v=m;flag[y]=1;
        }
        else{
            if(!tree[y].l)tree[y].l=x;
            else if(!tree[y].r)tree[y].r=x;
            tree[x].v=m;flag[x]=1;
        }
    }
    for(int i=1;i<=n;i++)
        if(!flag[i]){root=i;break;}
    dfs(root,q+1);
    printf("%d",f[root][q+1]);
}