這個題的數據真的很毒瘤,身爲一個交了8遍的蒟蒻的吶喊(嚶嚶嚶)
個人認爲作爲一個樹狀DP的入門題十分合適,同時建議做完這個題之後再去做一下這個題 選課
同時在這裏掛一個選取節點型樹形DP的狀態轉移方程:
for(int i=0;i<a[rt].size();i++) { int j=a[rt][i]; dp(j); f[rt][0]+=max(f[j][0],f[j][1]); f[rt][1]+=f[j][0]; }
(PS. j 爲 i 的孩子)
f[i][0]表示不選i
f[i][1]表示選i
使用一個vector來表明rt的所有孩子
題目描述:
Chloe和Vladik參加的奧林匹克運動會的慷慨贊助商允許所有參與者自己選擇獎品。聖誕節即將到來,所以贊助商決定用他們的獎品裝飾聖誕樹。
他們給參賽者拿了N個獎品,並在每個獎品上寫上一個唯一的ID(從1 1到N的整數)。
禮物i i的特點是整數ai--禮物的愉悅。禮物的愉悅可以是積極的、消極的或零的。贊助商把禮物11放在樹頂上。所有其他禮物都掛在一根繩子上,綁在其他禮物上,這樣每個禮物都掛在第一個禮物上,可能有一系列繩子和其他禮物。形式上,禮物形成了一個有n個頂點的樹根。
禮物的愉悅。禮物的愉悅可以是積極的、消極的或零的。贊助商把禮物11放在樹頂上。所有其他禮物都掛在一根繩子上,綁在其他禮物上,這樣每個禮物都掛在第一個禮物上,可能有一系列繩子和其他禮物。形式上,禮物形成了一個有n個頂點的樹根。
頒獎程序是這樣的:參加者一個接一個地來到樹上,從剩下的禮物中選擇一個,剪斷這個獎品掛着的繩子。請注意,所有用於在所選獎品上懸掛其他獎品的繩子都不會被割斷。所以參賽者會得到所選的禮物以及所有掛在上面的禮物,可能還有一系列繩子和其他禮物。
我們的朋友,克洛伊和弗拉基克,分享了奧運會的第一名,他們將同時選擇獎品!爲了不打架,他們決定選擇兩種不同的禮物,這樣掛在上面的禮物就不會相互交叉。換句話說,不應該有任何禮物掛在克洛伊選擇的禮物和弗拉基克選擇的禮物上。從所有可能的變種中,他們將選擇這樣的一對獎品,即他們在剪斷繩子後所得到的所有禮物的快樂之和儘可能大。
打印Vladik和Chloe能得到的最大快感。如果他們不可能在不打架的情況下選擇禮物,那麼就不可能打印出來。
輸入輸出格式:
第一行包含一個整數n(1<=n<=2.10^5)-禮物數量
下一行包含n個整數a1,a2,…,an,(-10^9<=ai<=10^9)-禮物的愉悅
下一行(n-1)分別包含兩個數字。這些行中的第i行包含整數u i和v i(1<=ui,vi<=n)-樹邊緣的描述。這意味着帶有數字ui和vi的禮物通過繩子相互連接。繩子描述中的禮物ID可以按任意順序給出:vi掛在ui上,ui掛在vi上
保證所有的禮物都掛在第一個禮物上,可能有一系列繩子和其他禮物。
一定要開long long!
下面開始解析:
這個題其實就是一個裸的樹狀DP;
但是這個題的實現的思想就是從下到上每個節點的dp值是以該節點爲原始節點的最大值
這就需要我們從下到上每一個點的子樹和都要求出來,然後我們再在這一個節點的所有子節點與這個點進行一次取最大值
最後我們針對於根節點求最大值就行;
個人認爲是一個樹狀DP的入門題 (光說不做誰都會嚶嚶嚶)
下面上蒟蒻醜陋的代碼:
(這個題dalao說可以有一種跑得更快的實現,不過本蒟蒻懶,就沒有再寫一遍,湊活着看吧qwq)
#include<iostream> #include<cstdio> #include<vector> #include<cmath> #include<algorithm> #include<set> #include<map> #define inf 2147483647888888 #define ll long long using namespace std; vector<ll> a[200866]; //使用vector數組來儲存節點,更方便於表達 ll b[200866]; ll f[200866]; //中轉 ll g[200866],x,y,n,ans=-inf; //最開始不要忘了要將ans重置爲-inf void dfs(ll fa,ll n) { if(a[n].size()==1&&n!=1) //如果遞歸到了邊界就退出遞歸 { g[n]=b[n]; //不要忘了這一步qwq return ; } ll sum=b[n]; //選取這個點 for(ll i=0;i<a[n].size();i++) //遍歷這個點的所有孩子,在這一些操作中選取 { ll pos=a[n][i]; if(pos==fa) continue; dfs(n,pos); sum+=g[pos]; //sum 和g起到了一種中轉同時也保證了原圖中的變量不發生變化 } g[n]=sum; } void GetDeep(ll fa,ll n) { if(a[n].size()==1&&n!=1) //如果遞歸到了邊界就退出 { f[n]=g[n]; return ; } f[n]=g[n]; ll maxn1=-inf,maxn2=-inf,sum=0; //這之間maxn2其實是起到了一箇中轉的作用,不要忘了初始化 for(ll i=0;i<a[n].size();i++) { ll pos=a[n][i]; if(pos==fa) //假如已經到達,就退出 continue; GetDeep(n,pos); //針對於下一層進行操作 if(f[pos]>maxn1) //如果這個點的下一個 { maxn2=maxn1; // 這個地方我調了半小時 maxn1=f[pos]; } else maxn2=max(maxn2,f[pos]); f[n]=max(f[n],f[pos]); } ans=max(ans,maxn1+maxn2); //這個地方就是一個求最大值 } int main() { scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lld",&b[i]); for(ll i=0;i<n-1;i++) { scanf("%lld%lld",&x,&y); //這裏我用了一個vector來儲存變量,這樣便可以更好地實現儲存 a[y].push_back(x); //兩個push的操作 a[x].push_back(y); } dfs(0,1); GetDeep(0,1); if(ans<-2147483647) //這個地方其實是一個很玄學的優化,因爲我初始置ans==-inf的時候會發現它這個點的初始值假如在沒有更改的地方也會有一定的更改 //但是它的初始值一定不會更改地很大 { printf("Impossible"); return 0; } printf("%lld",ans); return 0; }
總結:樹狀DP真的十分優美,前提是要學會了qwq
完結撒花
手寫不易qwq