【洛谷P2607】騎士 沒有上司的舞會+

題目大意:給定一個 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;
}
相關文章
相關標籤/搜索