1. 程式人生 > >Anniversary party(hdu1520)(poj2342)題解

Anniversary party(hdu1520)(poj2342)題解

原題地址:http://poj.org/problem?id=2342

題目大意:

  上司和下屬不能同時參加派對,求參加派對的最大活躍值。

  關係滿足一棵樹,每個人都有自己的活躍值(-128~127)

  求最大的活躍度。


 

樹形DP入門題。

首先講解一下樹形DP,顧名思義,樹形DP一定是在樹上的DP,與普通的DP相似,具有兩個方向。

1.根---->葉

2.葉---->根

其中第二種最為常用。實現方法:從根節點開始DFS(深度優先搜尋),一直搜尋到葉節點,然後根據其特殊性質賦值。通過回溯更新到根節點。得出答案。

回過來說這道題。

首先是狀態。

這道題的DP狀態共兩維,第一維表示自己本身編號,第二維表示去或者不去。

即dp[i][j] j的取值為0或1,1表示去,0表示不去。

最後的答案即為根節點的狀態max(dp[root][0],dp[root][1]);

首先常規操作,建邊,找根節點。有向圖,我們可以通過統計入度和出度來判斷根節點和葉節點。

接下來是狀態轉移方程:

 

dp[x][1]+=dp[to][0];
dp[x][0]+=max(dp[to][0],dp[to][1]);

很好理解,x是上司,to是下屬,通過下屬來更新上司。

一個上司可能有多個下屬,但是這些下屬只能有一個上司,符合樹的定義。

如果這個上司去,那麼下屬只能不去,所以要加上所有的下屬不去的狀態。即dp[to][0]

如果上司不去,這個時候需要比較下屬去或者不去的大小。

有的同學可能會問了,為什麼不直接讓下屬去多好啊,一步貪心,相當於把樹進行了黑白染色,要麼黑的去,要麼白的去,比較一下大小不就好了?

我剛開始糾結了半天,後來發現了題中所給的條件。

關係滿足一棵樹,每個人都有自己的活躍值(-128~127)。

由於有些員工過於矯情,去派對自己還不活躍,所以之前的貪心是錯誤的。

之後比較老總(根節點)去或者不去的大小,選擇較大值。

上程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
int val[7000]; int indegree[7000]; int outdegree[7000]; struct edge { int to; int nxt; }eg[7000]; int head[7000]; int cnt = 1; void add(int x,int y) { eg[cnt].to = y; eg[cnt].nxt = head[x]; head[x] = cnt++; } int dp[7000][3]; void dfs(int x) { if(outdegree[x]==0) { dp[x][1] = val[x]; return ; } for(int i = head[x];i;i=eg[i].nxt) { int to = eg[i].to; dfs(to); dp[x][1]+=dp[to][0]; dp[x][0]+=max(dp[to][0],dp[to][1]); } } int main() { int n; scanf("%d",&n); for(int i = 1;i<=n;i++) { scanf("%d",&val[i]); dp[i][1] = val[i]; } for(int i = 1;i<=n;i++) { int x,y; scanf("%d%d",&x,&y); if(x==0&&y==0) { break; } indegree[x]++; outdegree[y]++; add(y,x); } int root; for(int i = 1;i<=n;i++) { if(indegree[i]==0) { root = i; } } dfs(root); printf("%d",max(dp[root][0],dp[root][1])); }