題目大意:給定一個 N 個點的外向樹森林,點有點權。從該樹中選出若干頂點組成一個集合,知足任意相鄰的兩個頂點不一樣時出如今該集合中,求這樣集合中點權和的最大值爲多少。node
題解:與樹相比,該題多了環這個結構。對於環上任意一條邊來講,邊的端點不可能同時被選取,所以,能夠選擇環上任意一條邊,將其斷開,答案不變。這樣就將環的問題轉化成了樹的問題,接着,分別以斷開邊的端點爲根,作兩次樹形 dp,將兩個不選根節點的權值最大值取最大加入答案貢獻便可。c++
另外,須要注意的是,相比較有向圖找環,無向圖因爲邊的雙向性,僅僅用 vis[u]=1 進行判斷會掛掉,所以,須要記錄下前一條邊,並保證後一條邊不等於前一條邊才行。這裏不選點是由於兩個點組成的環的狀況。
另外,用並查集進行找環也是能夠的。git
代碼以下spa
#include <bits/stdc++.h> using namespace std; const int maxn=1e6+10; inline int read(){ int x=0,f=1;char ch; do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch)); do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch)); return f*x; } struct node{ int nxt,to; }e[maxn<<1]; int tot=1,head[maxn]; inline void add_edge(int from,int to){ e[++tot]=node{head[from],to},head[from]=tot; } int n,a,b,edge,val[maxn],vis[maxn]; long long ans,dp[maxn][2]; void read_and_parse(){ n=read(); for(int i=1,to;i<=n;i++)val[i]=read(),to=read(),add_edge(i,to),add_edge(to,i); } void find(int u,int pre){ vis[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;if(i==(pre^1))continue; if(!vis[v])find(v,i); else if(vis[v]==1)a=u,b=v,edge=i; } vis[u]=2; } void dfs(int u,int fa){ dp[u][1]=val[u],dp[u][0]=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa||i==edge||i==(edge^1))continue; dfs(v,u); dp[u][1]+=dp[v][0]; dp[u][0]+=max(dp[v][0],dp[v][1]); } } void solve(){ for(int i=1;i<=n;i++)if(!vis[i]){ find(i,-1); dfs(a,0); long long res1=dp[a][0]; dfs(b,0); long long res2=dp[b][0]; ans+=max(res1,res2); } printf("%lld\n",ans); } int main(){ read_and_parse(); solve(); return 0; }