樹型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]);
}