藍橋杯15屆第十題——生命之樹(dfs, vector)

前言:題目看似簡單,但是對於第一次接觸這類題型的同學還是會存在着一定的困難。
這類題型是典型的樹型遍歷問題。若我們能夠將樹都按照路徑遍歷一遍,然後一直維護一個權值最大的子集即可。

生命之樹題目

在X森林裏,上帝創建了生命之樹。
他給每棵樹的每個節點(葉子也稱爲一個節點)上,都標了一個整數,代表這個點的和諧值。
上帝要在這棵樹內選出一個非空節點集S,使得對於S中的任意兩個點a,b,都存在一個點列 {a, v1, v2, …, vk, b} 使得這個點列中的每個點都是S裏面的元素,且序列中相鄰兩個點間有一條邊相連。
在這個前提下,上帝要使得S中的點所對應的整數的和儘量大。
這個最大的和就是上帝給生命之樹的評分。
經過atm的努力,他已經知道了上帝給每棵樹上每個節點上的整數。但是由於 atm 不擅長計算,他不知道怎樣有效的求評分。他需要你爲他寫一個程序來計算一棵樹的分數。
「輸入格式」
第一行一個整數 n 表示這棵樹有 n 個節點。
第二行 n 個整數,依次表示每個節點的評分。
接下來 n-1 行,每行 2 個整數 u, v,表示存在一條 u 到 v 的邊。由於這是一棵樹,所以是不存在環的。
「輸出格式」
輸出一行一個數,表示上帝給這棵樹的分數。
「樣例輸入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
「樣例輸出」
8


題目描述了一棵無根樹,我們可以給樹建立一個根,將樹都按照路徑遍歷一遍,然後一直維護一個權值最大的子集即可。
具體如下:
①、一個節點走到下一個節點的路徑是不定的,那我們如何存儲路徑?
我們可以利用動態數組vector來存儲路徑信息。

vector <ll> g[MAXN+1]; //鄰接表:記錄相鄰的結點
					   //MAXN+1:節點數
					   //g[a].size():a節點對應的路徑數

②、如何設計dfs?
w數組:記錄每個點的權重。
ww數組:記錄每個點作爲根節點時能得到的最大權和。

首先,我們要選擇一個節點作爲樹的起始根。
第二,不斷試探路徑。若下一個結點最大權重大於0,則將下一個結點上的權重賦給結點。
第三,記錄最大的權和ans。

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
ll n;
const ll MaxN=1e5;
ll w[MaxN+1];			//每個點的權重 
ll ww[MaxN+1];			//每個點作爲根節點時能得到的最大權和
ll ans=0; 
vector <ll> g[MaxN+1];	//鄰接表:記錄相鄰的結點,不確定相鄰結點數

/*以root爲根,算出最大的權和*/
void dfs(ll root, ll fa)	//無根樹轉有根樹
{
	ww[root]=w[root];		//節點初值 
	
	for(int i=0; i<g[root].size(); ++i)	//g[root].size():與root相關的路徑數 
	{
		ll son = g[root][i];	//其中一個孩子
		if(son!=fa)				//防止返回 
		{
			dfs(son, root);		
			if(ww[son]>0)		//以son爲根的路徑產生的和諧值大於0則保留,否則捨棄 
			ww[root]+=ww[son];
		}
	}
	if(ww[root]>ans) ans=ww[root];//保留路徑產生最大的和諧值
} 

int main()
{
	//輸入 
	cin>>n;
	for(ll i=1; i<=n; i++)
	cin>>w[i];
	for(ll j=1; j<n; j++)	//n-1條路徑 
	{
		ll u, v;
		cin>>u>>v;
		g[u].push_back(v);	//使用vector來存儲來回路徑 
		g[v].push_back(u);	//雙向
	}
	dfs(1, 0);		//將w[1]作爲根
	cout<<ans;
	
	return 0;
}

希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白。

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