1. 程式人生 > >樹形DP學習筆記

樹形DP學習筆記

說實話,樹形DP本不應該單獨拿出來說,因為它本質就是一個動態規劃。而且與一般的樹形結構不同,樹形DP不會去用臨接表建圖。這可能也是樹形DP沒有一個比較基礎的教程的原因。

搞清楚了動態規劃就搞清楚了樹形DP

一道入門題也許可以讓自己對樹形dp有一個粗略的認識

luogu P1352
題目

題目描述

某大學有N個職員,編號為1~N。他們之間有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。現在有個週年慶宴會,宴會每邀請來一個職員都會增加一定的快樂指數Ri,但是呢,如果某個職員的上司來參加舞會了,那麼這個職員就無論如何也不肯來參加舞會了。所以,請你程式設計計算,邀請哪些職員可以使快樂指數最大,求最大的快樂指數。

輸入輸出格式

輸入格式:

第一行一個整數N。(1<=N<=6000)

接下來N行,第i+1行表示i號職員的快樂指數Ri。(-128<=Ri<=127)

接下來N-1行,每行輸入一對整數L,K。表示K是L的直接上司。

最後一行輸入0 0

輸出格式:

輸出最大的快樂指數。

輸入輸出樣例

輸入樣例:

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

輸出樣例:

5
分析

題中很明確的告知,學校裡的職員關係像一顆樹一樣,以校長為根

於是,抽象出模型,就是一顆每個點都有權值的樹

然後,題中給了一個條件

,若某個節點u被選用,其子節點v都不可被選用

題中的問題是,要在滿足上述條件的要求下,得到一個最大權值

由於每個節點只有選和不選兩種狀態,於是設f[u][1]指v號節點被選用後的最大值,f[u][0]是指v號節點沒被選用時的最大值

對於選u號節點來說,u號節點的所有直接子節點v都不能選

對於不選u號節點來說,他的子節點v可選可不選

此時不難得到狀態轉移方程如下
f [ u ]

[ 1 ] = s u m ( f [ v ] [ 0 ] ) + h a p p y [ u ] f[u][1]=sum(f[v][0])+happy[u]
f [ u ] [ 0 ] = s u m ( m a x ( f [ v ] [ 0 ] , f [ v ] [ 1 ] ) ) f[u][0]=sum(max(f[v][0],f[v][1]))

分析狀態轉移方程可知,要知道節點u的狀態,必須要知道其子節點v的狀態,於是我們就有兩種實現方法:從底層迴圈或直接dfs,這兩種實現方法中dfs可能比較好打,但從底層迴圈不會有爆棧的風險

程式碼(dfs)
#include<bits/stdc++.h>
using namespace std;
#define frein(txtname); freopen(txtname,"r",stdin); 
#define loop(i,start,end) for(int i=start;i<=end;i++)
#define clean(arry,num); memset(arry,num,sizeof(arry));
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
int n,master;
const int maxn=6000+10;
int happy[maxn];
vector<int>follow[maxn];
bool vis[maxn];
int dp[maxn][2];
inline int read()
{
   int ans=0;bool negive=false;char r=getchar();
   while(r<'0'||r>'9'){if(r=='-')negive=true;r=getchar();}
   while(r>='0'&&r<='9'){ans=ans*10+r-'0';r=getchar();}
   return (negive)?-ans:ans;
}
void datasetting()
{
	n=read();
	loop(i,1,n)happy[i]=read();
	clean(vis,false);
	int k,l;l=read();k=read();
	while(l*k)
	{
		follow[k].push_back(l);
		vis[l]=true;
		l=read();k=read();
	}
	loop(i,1,n)if(!vis[i]){master=i;break;}
}
void dfs(int x)
{
	dp[x][0]=0;
	dp[x][1]=happy[x];
	int len=follow[x].size();
	loop(i,0,len-1)
	{
		int son=follow[x][i];
		dfs(son);
		dp[x][0]+=max(dp[son][1],dp[son][0]);
		dp[x][1]+=dp[son][0];
	}
}
int main()
{
   frein("datain.txt");
   datasetting();
   dfs(master);
   printf("%d",max(dp[master][0],dp[master][1]));
   return 0;
}
/********************************************************************
   ID:Andrew_82
   LANG:C++
   PROG:Tree shape dynamic programming
********************************************************************/

鄙人知識淺薄,望各位大佬斧正