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