首先題意很清楚了,就是找兩顆不想關的子樹,使其權值和最大。
這是一道樹形dp,也是我做的第一道樹形dp,非常有紀念意義,所以在這裏寫一下。
寫法非常智障,說明自己姿勢水平還是太低。
先貼代碼。
#include<bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
const long long INF=10000000000000000;//這裏要設大一點因爲題目數據很大
long long ans,max1,max2;
int n;
vector<int> G[maxn],B[maxn];//B用來臨時存儲,G中存的樹要求是有序的,即一條邊只加一次
long long value[maxn],sum[maxn],dp[maxn];
void dfs1(int x)//calculate the sum of the subtree of x,計算每個節點子樹的權值和sum
{
sum[x]=value[x];
for(auto it:G[x])
{
dfs1(it);
sum[x]+=sum[it];
}
}
void dfs2(int x)//calculate the largest sum of sub-trees of x,計算每個節點i的後代(包括i)中,sum最大的,記作dp【i】
{
dp[x]=sum[x];
for(auto it:G[x])
{
dfs2(it);
dp[x]=max(dp[x],dp[it]);
}
}
void doit(long long &max1,long long &max2,long long x)//update max1 and max2,max1爲最大,max2爲次大,x爲新接受的元素
{
if(x<=max2)
return ;
if(x>=max1)
{
max2=max1;
max1=x;
return ;
}
{
max2=x;
return ;
}
}
void dfs(int x,int fa)//瞎幾把遍歷,生成G
{
for(auto i:B[x])
{
if(i==fa)
continue;
G[x].push_back(i);
dfs(i,x);
}
}
int main(void)
{
int u,v;
cin>>n;
for(int i=1;i<=n;i++)
cin>>value[i];
for(int i=1;i<n;i++)
{
cin>>u>>v;
B[u].push_back(v);
B[v].push_back(u);
}//讀入,暫時存着,因爲給的節點關係是無序的,需要經過預處理,使得其有序
dfs(1,-1);//to ensure the father and child relation:G[u]=v then v is child of u
dfs1(1);
dfs2(1);
ans=-INF;
for(int i=1;i<=n;i++)
{
if(G[i].size()>=2)
{
max1=max(dp[G[i][0]],dp[G[i][1]]);//the largest
max2=min(dp[G[i][0]],dp[G[i][1]]);//the second largest
for(int j=2;j<G[i].size();j++)
{
doit(max1,max2,dp[G[i][j]]);
}
ans=max(ans,max1+max2);
}
else
continue;
}
if(ans==-INF)
cout<<"Impossible";
else
cout<<ans;
return 0;
}
這道題我的時間控制的不是很好,因爲加了一個預處理,把樹按照標準形式存到了G裏。