1. 程式人生 > >【POJ - 2342】Anniversary party(樹形dp)

【POJ - 2342】Anniversary party(樹形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 N – 1 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

題意:

你需要選一部分人去派對,每個人都有一個權值,但是員工和他的直接主管不能同時選,問最大的權值和是多少。

思路:

第一次做的樹形dp,沒啥思路,看了別人的程式碼後,感覺有了點理解。這道題目中先算員工再算他的主管,先確定葉節點去不去的最大權值,再求他的直接上級時,就根據其子節點的去不去的權值推出,就這樣一直往上推,因此這裡用了dfs,而且這裡求根節點的方法也可以記一下(自己沒想到)。

poj這道題目是單組輸入的,hdu也有一道同樣的題是多組輸入的。

ac程式碼:

/*
這一道題目是由子節點向根節點來推的
*/
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<iostream>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
#define eps 1e-8
using namespace std;
struct node{
	int v;
	int next;
}side[12000];
int head[7000];
int cnt=0;
void init()
{
	cnt=0;
	memset(head,-1,sizeof(head));
}
void add(int x,int y)
{
	side[cnt].v=y;
	side[cnt].next=head[x];
	head[x]=cnt++;
}
//dp[i][1]表示選第i個人事達到的最大值,dp[i][0]表示不選第i個人達到的最大值 
int dp[7000][2];// 每個人有兩個狀態 
void dfs(int cur)//cur指的是父節點 
{
	for(int i=head[cur];i!=-1;i=side[i].next)
	{
		int ty=side[i].v;//子節點 
		dfs(ty); 
		dp[cur][1]+=dp[ty][0];
		dp[cur][0]+=max(dp[ty][1],dp[ty][0]);
	}
	return ;
}
int main()
{
	init();
	int n;
	scanf("%d",&n);
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=n;i++)
	scanf("%d",&dp[i][1]);
	int u,v;
	int root=(n)*(n+1)/2;//根節點,一開始是所有人的編號之和 
	while(~scanf("%d%d",&u,&v),u+v)
	{
		add(v,u);//加邊 
		root-=u;
		//因為每個人只能有一個直屬上司,即只有一個父節點,所以不是最高領導的人都會在u的位置出現一次,且只有一次,
		//之前已經把所有人的編號都加和,再減去不符合條件的人的編號,就是要求的根節點的編號 。 
	}
	dfs(root);//從根節點往下搜,搜到葉節點,在向上推 
	cout<<max(dp[root][1],dp[root][0])<<endl;//最後的資訊都會彙集到根節點,因此只用看看根節點的兩個資訊是否符合。 
	return 0;
}