AcWing 285 沒有上司的舞會(入門樹形dp)

**
原題鏈接

題目描述

**Ural大學有N名職員,編號爲1~N。

他們的關係就像一棵以校長爲根的樹,父節點就是子節點的直接上司。

每個職員有一個快樂指數,用整數 Hi 給出,其中 1≤i≤N。

現在要召開一場週年慶宴會,不過,沒有職員願意和直接上司一起參會。

在滿足這個條件的前提下,主辦方希望邀請一部分職員參會,使得所有參會職員的快樂指數總和最大,求這個最大值。

輸入格式
第一行一個整數N。

接下來N行,第 i 行表示 i 號職員的快樂指數Hi。

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

輸出格式
輸出最大的快樂指數。

數據範圍
1≤N≤6000,
−128≤Hi≤127

輸入樣例:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
輸出樣例
5

題目分析

對於這個題目我們首先要做的就是建圖(建樹),每個點都有一個權值,但是選擇的時候,我們子節點和父節點不能一起選,問選擇的可以最大權值是多少?
拿樣例來說,我們可以建出這樣一顆樹

紅色區域爲最佳選擇部分。
我們可以設f(i)爲根節點爲i的時候最佳選擇,這時發現無法進行狀態轉移,因爲每個父親節點有兩種狀態,選或者不選,對子節點的影響是不同的
1 選父親節點的時候。子節點不能選,
2不選父親節點的時候。字節點可以選,可以不選
所以這裏我們加一維,f[i][1]和f[i][0] 分別表示 選父親節點和不選父親節點的情況

狀態轉移
在這裏插入圖片描述

對於一個節點i有着兩個子節點s1 ,s2,

1如果父親節點i選的話,
f[i][1]=f[s1][0]+f[s2][0];
2如果父親節點i不選的話,
f[i][0]=max(f[s1][0],f[s1][1])+max(f[s2][0],f[s2][1])

我們注意題目中由n個點。n-1一條邊,所以一定是一棵樹,
我採取了類似鏈式前向星的方法存圖
AcCode

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define  gold_m main
#define re register
#define Accept return 0;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf =0x3f3f3f3f;
const int maxn=1e6+5;
const int mod = 1e9+7;
const int N=666;
const long double PI=3.1415926535897932384626433832795;
const long double e=2.71828182845904523536028747135266;
typedef pair<int,int>PII;
inline ll read() {
	ll  x=0;
	bool f=0;
	char ch=getchar();
	while (ch<'0'||'9'<ch)	f|=ch=='-', ch=getchar();
	while ('0'<=ch && ch<='9')
		x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
priority_queue<int,vector<int>, greater<int> >heap;
stack<int>st;
int  dp[maxn][2],cnt,root,head[maxn],h[maxn],n,l,k,rt[maxn];
struct wmy {
	ll u,v,w,next;
} a[maxn];
void add(ll u,ll v) {
	a[cnt].u=u;
	a[cnt].v=v;
//	a[cnt].w=w;
	a[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int  u)
{
	dp[u][1] =h[u];
	
	for(int i=head[u];i!=-1;i=a[i].next)
	{
		ll v=a[i].v;
		dfs(v);
		dp[u][1]+=dp[v][0];
		dp[u][0]+=max(dp[v][0],dp[v][1]);
	}
	return ;
}
int   gold_m() {
	memset(head,-1,sizeof(head));
	n=read();
	for(int i=1 ; i<=n ; i++) h[i]=read();
	for(int i=1 ; i<n; i++) {
		cin>>l>>k; //有一條由k指向l的線段 
		add(k,l);
		rt[l]++;
	}
	for(int i=1 ; i<=n; i++) {
		if(rt[i]==0) {
			root=i;// 如果這個點沒有父親節點,那麼這個點一定是根節點,校長節點 
			break;
		}
	}
	dfs(root);
	cout<<max(dp[root][0],dp[root][1]);
	Accept;
}
/*

*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章