[bzoj1040][ZJOI2008]骑士——环套树+DP 大佬们的博客 Some Links

版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/81980603

题目大意:

  Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

思路:

一个人痛恨另外一个人就在他们两个之间连一条边,不难发现最后就是一个图然后我们不能选择有边相邻的点。
再具体一点,这个图有n个点n条边,又因为每个点都保证有边,所以最后就是一个环套树森林,我们只需要在这个上面DP即可。
一般的方法是把外向树处理出来之后在环上DP,其实可以没有必要这样。考虑便一条基环上边(u,v),最后的答案中u和v必定有一个没有选到,假设最后的答案中u没有选到,那么答案必定是在u不选情况下的最大值,这样的话我们就只需要在环上任取一条边分别对边上的两个点做一次不选这个点的DP即可,保证最后的答案必定在其中。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj1040.in","r",stdin);
    freopen("bzoj1040.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=1e6+10;
int n,w[maxn];
ll ans,dp[maxn][2];
int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1;
bool vis[maxn],flag;

void add(int u,int v){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u;
}

void solve(int u,int f){
    dp[u][1]=w[u]; dp[u][0]=0;
    for(int i=beg[u];i;i=las[i]){
        if(to[i]==f || !to[i])continue;
        solve(to[i],u);
        dp[u][0]+=max(dp[to[i]][0],dp[to[i]][1]);
        dp[u][1]+=dp[to[i]][0];
    }
}

void dfs(int u,int fr){
    if(vis[u]){
        flag=true;
        int v=to[fr^1];
        ll Max=0;
        to[fr]=to[fr^1]=0;
        solve(u,0);
        Max=dp[u][0];
        solve(v,0);
        Max=max(Max,dp[v][0]);
        ans+=Max;
        return;
    }
    vis[u]=1;
    for(int i=beg[u];i;i=las[i]){
        if(i==(fr^1))continue;
        dfs(to[i],i);
    }
}

int main(){
    File();
    read(n);
    int u;
    REP(i,1,n)read(w[i]),read(u),add(i,u);
    REP(i,1,n)if(!vis[i]){
        flag=false;
        dfs(i,0);
        if(!flag)solve(i,0),ans+=max(dp[i][0],dp[i][1]);
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81980603