蓝桥杯 - 生命之树(树形dp)
在X森林里,上帝創(chuàng)建了生命之樹。
?
他給每棵樹的每個(gè)節(jié)點(diǎn)(葉子也稱為一個(gè)節(jié)點(diǎn))上,都標(biāo)了一個(gè)整數(shù),代表這個(gè)點(diǎn)的和諧值。
上帝要在這棵樹內(nèi)選出一個(gè)非空節(jié)點(diǎn)集S,使得對(duì)于S中的任意兩個(gè)點(diǎn)a,b,都存在一個(gè)點(diǎn)列 {a, v1, v2, ..., vk, b} 使得這個(gè)點(diǎn)列中的每個(gè)點(diǎn)都是S里面的元素,且序列中相鄰兩個(gè)點(diǎn)間有一條邊相連。
?
在這個(gè)前提下,上帝要使得S中的點(diǎn)所對(duì)應(yīng)的整數(shù)的和盡量大。
這個(gè)最大的和就是上帝給生命之樹的評(píng)分。
?
經(jīng)過atm的努力,他已經(jīng)知道了上帝給每棵樹上每個(gè)節(jié)點(diǎn)上的整數(shù)。但是由于 atm 不擅長計(jì)算,他不知道怎樣有效的求評(píng)分。他需要你為他寫一個(gè)程序來計(jì)算一棵樹的分?jǐn)?shù)。
?
「輸入格式」
第一行一個(gè)整數(shù) n 表示這棵樹有 n 個(gè)節(jié)點(diǎn)。
第二行 n 個(gè)整數(shù),依次表示每個(gè)節(jié)點(diǎn)的評(píng)分。
接下來 n-1 行,每行 2 個(gè)整數(shù) u, v,表示存在一條 u 到 v 的邊。由于這是一棵樹,所以是不存在環(huán)的。
?
「輸出格式」
輸出一行一個(gè)數(shù),表示上帝給這棵樹的分?jǐn)?shù)。
?
「樣例輸入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
?
「樣例輸出」
8
?
「數(shù)據(jù)范圍」
對(duì)于 30% 的數(shù)據(jù),n <= 10
對(duì)于 100% 的數(shù)據(jù),0 < n <= 10^5, 每個(gè)節(jié)點(diǎn)的評(píng)分的絕對(duì)值不超過 10^6 。
題目分析:題意一開始沒讀懂,去問的zx學(xué)長,簡單來說就是從樹中求一個(gè)最大連通子塊,保證這個(gè)連通子塊的權(quán)值和最大,因?yàn)檎f子樹不太合適,就叫做連通子塊啦
題目給到了1e5的數(shù)據(jù),所以暴力是肯定不能考慮的了,因?yàn)樯婕暗綑?quán)值和的問題,所以我們不難聯(lián)想到前綴和,在樹上維護(hù)前綴和不就是樹形dp嘛,但如果是單純的維護(hù)前綴和的話,又不太好更新答案,所以我們考慮一下如何設(shè)計(jì)dp
想了半天還是沒有思路啊,但zx學(xué)長看了一眼就給秒掉了,再次不得不承認(rèn)人與人之間的差距。。
這個(gè)題目給出的是一棵無根樹,我們依然可以當(dāng)做有根樹來做,隨意找一個(gè)節(jié)點(diǎn)當(dāng)做根,跑一遍dfs,dp[i]代表節(jié)點(diǎn)i為上邊界的子連通塊的最大值,因?yàn)槭亲舆B通塊而不是子樹,所以在任意節(jié)點(diǎn)我們是可以直接斷開的,為什么要斷開呢?因?yàn)榧偃绻?jié)點(diǎn)i的某棵子樹做出的貢獻(xiàn)是負(fù)數(shù),那么我們完全可以舍棄掉這棵子樹,從而使得每次轉(zhuǎn)移的答案最優(yōu),這樣轉(zhuǎn)移方程也就很容易了:
dp[u]=max(dp[v],0);v為u的所有子節(jié)點(diǎn)
維護(hù)完dp后O(n)跑一遍最大值就是答案啦
這里需要注意一下,一開始我理解的dp[i]的含義有誤,所以導(dǎo)致整個(gè)思路混亂,從而沒有正確的設(shè)計(jì)出轉(zhuǎn)移方程,我一開始以為dp[i]代表的是到節(jié)點(diǎn)i為止,子連通塊中的最大值,從而出現(xiàn)了另一個(gè)疑問:如果最大值是在沿著節(jié)點(diǎn)i的父節(jié)點(diǎn)那條鏈上該怎么辦呢?其實(shí)并不用擔(dān)心這個(gè)問題,如果包含最大值的那一坨真的出現(xiàn)在節(jié)點(diǎn)i父節(jié)點(diǎn)的那條鏈上,雖然我們不能用節(jié)點(diǎn)i來更新最大值,但總有節(jié)點(diǎn)是可以更新到這個(gè)最大值的,我們可以這樣理解,節(jié)點(diǎn)i作為最大值這一坨的下界,自然無法更新答案,但只要是這個(gè)最大值合法,那么他必定存在一個(gè)上界,也是屬于這棵樹中的任意一個(gè)節(jié)點(diǎn),當(dāng)遍歷到上界的這個(gè)節(jié)點(diǎn)的時(shí)候,就可以更新相應(yīng)的最大值了,所以在這里個(gè)人認(rèn)為dp[i]理解為以節(jié)點(diǎn)i為上界的最大子連通塊比較合適
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> #include<unordered_map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node[N];LL dp[N];int val[N];void dfs(int u,int f) {dp[u]=val[u];for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==f)continue;dfs(v,u);dp[u]+=max(dp[v],0LL);//狀態(tài)轉(zhuǎn)移方程} }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",val+i);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);node[x].push_back(y);node[y].push_back(x);}dfs(1,-1);LL ans=-inf;for(int i=1;i<=n;i++)ans=max(ans,dp[i]);printf("%d\n",ans);return 0; }?
總結(jié)
以上是生活随笔為你收集整理的蓝桥杯 - 生命之树(树形dp)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中石油训练赛 - Block(二维前缀和
- 下一篇: 蓝桥杯 - 牌型种数(dfs)