1. 程式人生 > >hdu -1520-Anniversary party (樹形DP)

hdu -1520-Anniversary party (樹形DP)

hdu -1520-Almost Arithmetical Progression(樹形DP)

There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure of employees. It means that the supervisor relation forms a tree rooted at the rector V. E. Tretyakov. In order to make the party funny for every one, the rector does not want both an employee and his or her immediate supervisor to be present. The personnel office has evaluated conviviality of each employee, so everyone has some number (rating) attached to him or her. Your task is to make a list of guests with the maximal possible sum of guests’ conviviality ratings.
Input
Employees are numbered from 1 to N. A first line of input contains a number N. 1 <= N <= 6 000. Each of the subsequent N lines contains the conviviality rating of the corresponding employee. Conviviality rating is an integer number in a range from -128 to 127. After that go T lines that describe a supervisor relation tree. Each line of the tree specification has the form:
L K
It means that the K-th employee is an immediate supervisor of the L-th employee. Input is ended with the line
0 0
Output
Output should contain the maximal sum of guests’ ratings.
Sample Input
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
Sample Output
5

  • 題目大意:每個點都有一個開心值,然後父節點和子節點不能同時存在,求怎麼選才能讓開心值最大;
    輸入 第一行n是點的個數,接下來n行是每個點的開心值,後面輸入 a b (直到0 0 結束)表示 b是a 的父節點
  • 解題思路:
    裸的樹形DP
    針對這個題 我們用一個 dp[i][j]來儲存狀態,dp[i][0]裡面存的是i結點不選時的最優解,dp[i][1]裡面存的是 i結點選的時候的最優解。
    然後我們再考慮下如何進行狀態轉移。我們用子節點來更新父結點,我們假設子節點裡面存的都是相應的最優解,遍歷i結點所有的子節點 j dp[i][0]+=max(dp[j][0],dp[j][1]) (如果父節點不取那麼子結點可以取也可以不取) dp[i][1]+=dp[j][0]; (如果父結點取,那子結點一定不能取)。
    怎麼讓這個子節點是最優解吶?那就遞迴下去呀~
for(int i=head[x];i!=-1;i=side[i].next)
	{
		int t=side[i].to;
		dfs(t);///讓子節點都安排正確
		dp[x][0]+=max(dp[t][0],dp[t][1]);//x不選的時候 子節點可以選也可以不選;
		dp[x][1]+=dp[t][0];//x選的時候 子節點一定不能選
	}

我們用靜態連結串列來存圖;

然後還有一個小細節的地方,我們如何確定從哪裡開始dfs呢?我們要找到一個根節點(沒有父節點的結點) 我們用一個fa[]陣列存一下每一個結點的父節點(初始化為-1)這樣我們可以用個while找到一個根節點

		int
root=1;//找到一個根節點 while(fa[root]!=-1) root=fa[root];
  • 樹形dp小結:
    就是把線性的dp搬到了一個樹上,用已經確定狀態的子節點來更新父節點,或者用父節點來更新子節點。最重要的也是dp的核心,當然就是狀態轉移方程啦,只要狀態轉移方程寫出來了,基本上題目也就解決了。寫狀態轉移方程的時候一定要注意,子問題一定得是已經解決了的問題,然後還要注意一些邊界條件。我理解也不是很深入,姑妄言之,讀者也是姑妄聽之。
  • 完整程式碼:

// dp[maxn][2]  dp[i][0]=i結點不選的時候的最優解,dp[i][1]表示i結點選的時候的最優解
//狀態轉移方程 dp[i][1]=dp[i][1]+max(dp[v][0])

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=6100;
struct node
{
	int to;
	int next;
}side[maxn*2];
int cnt=0;
int head[maxn];
int n;
int value[maxn];///用來記錄每個人的歡樂值
int fa[maxn];
int dp[maxn][2];///dp[i][0]=i結點不選的時候的最優解,dp[i][1]表示i結點選的時候的最優解
void init()
{
	memset(head,-1,sizeof(head));
	memset(fa,-1,sizeof(fa));
	cnt=0;
}
void add(int x,int y)
{
	side[cnt].to=y;
	side[cnt].next=head[x];
	head[x]=cnt++;
}
void dfs(int x)
{
	if(head[x]==-1)
	{
		dp[x][1]=value[x];
		dp[x][0]=0;
		return;
	}
	dp[x][0]=0;
	dp[x][1]=value[x];
	for(int i=head[x];i!=-1;i=side[i].next)
	{
		int t=side[i].to;
		dfs(t);///讓子節點都安排正確
		dp[x][0]+=max(dp[t][0],dp[t][1]);//x不選的時候 子節點可以選也可以不選;
		dp[x][1]+=dp[t][0];//x選的時候 子節點一定不能選
	}
}
int main()
{
	while(~scanf("%d",&n))
	{
		init();
		for(int i=1;i<=n;i++)
			cin>>value[i];
		int a,b;
		while(scanf("%d%d",&a,&b))
		{
			if(a==0&&b==0)
				break;
			add(b,a);///注意父子關係
			fa[a]=b;
		}

		int root=1;//找到一個根節點
		while(fa[root]!=-1)
			root=fa[root];

		dfs(root);
		int ans=max(dp[root][0],dp[root][1]);
		cout<<ans<<endl;
	}
}