【树】【独立集】【动态规划】[BZOJ1040][ZJOI2008]骑士

题目描述

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

第一行包含一个正整数N,描述骑士团的人数。接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。

应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。

样例输入

3
10 2
20 3
30 1

样例输出

30

题目分析

首先我们可以通过观察发现,两个人如果有任意的一个人讨厌另一个的话那么不能就不能够同时选择,那么我们可以想到的和这种选择有关的:1.二分图匹配 2.树上的最大独立集, 那么我们可以发现如果我们选择二分图匹配,我们是无法考虑到每个人的攻击力和该图是否为二分图的,那么我们选择树上的最大独立集,那么可以发现这张图我们先看作一个无向联通图,那么每个人指向一个人那么总的边数就是n对于一棵树我们有(n-1)条边,那么该图中一定存在有且仅有一个环,我们的任务就是找到这个环上的任意一条边,然后把这条边断开,对于这条边的两个端点,我们分别考虑不选择自己的最优解(也就是断开后的带权值的独立集)我们分别用两个端点为根做一次,然后 ans=max(dp[l][0],dp[r][0]) 这里的0代表不选择,这个仔细想一下就可以明白,那么如果输入有多个图怎么办,我们可以发现任意一个图中有且仅有一个环在输入的时候我们用并查集处理一下,如果无法插入,那么说明构成了一个环,我们不连接并且存储当前的两个端点编号就行了。最后对每个图分别求答案求和

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 1000000;
const int MAXM = 1000000;
struct node{
    int v;
    node *next;
}Edges[MAXM*2+10], *ecnt=Edges, *adj[MAXN+10];
bool vis[MAXN+10], stp;
int Fa[MAXN+10];
vector<int> l1, l2;
int Findfa(int u){
    if(Fa[u]) return (Fa[u] = Findfa(Fa[u]));
    return u;
}
void addedge(int u, int v){
    ++ecnt;
    ecnt->v = v;
    ecnt->next = adj[u];
    adj[u] = ecnt;

    ++ecnt;
    ecnt->v = u;
    ecnt->next = adj[v];
    adj[v] = ecnt;
}
int val[MAXN+10];
long long dp[MAXN+10][2];
void dfs2(int u, int fa){
    dp[u][0] = 0;
    for(node *p=adj[u];p;p=p->next){
        if(p->v == fa) continue;
        dfs2(p->v, u);
    }
    dp[u][1] = 1LL * val[u];
    for(node *p=adj[u];p;p=p->next){
        if(p->v == fa) continue;
        dp[u][1] += dp[p->v][0];
        dp[u][0] += max(dp[p->v][0], dp[p->v][1]);
    }
}
int main(){
    freopen("knight.in", "r", stdin);
    freopen("knight.out", "w", stdout);
    int n, h, f1, f2;
    scanf("%d", &n);
    for(int i=1;i<=n;i++){
        scanf("%d%d", &val[i], &h);
        f1 = Findfa(i), f2 = Findfa(h);
        if(f1 == f2){
            l1.push_back(i);
            l2.push_back(h);
        }
        else{
            Fa[f1] = f2;
            addedge(i, h);
        }
    }
    long long ans = 0;
    int sz = l1.size();
    for(int i=0;i<sz;i++){
        dfs2(l1[i], -1);
        long long tans = dp[l1[i]][0];
        dfs2(l2[i], -1);
        tans = max(tans, dp[l2[i]][0]);
        ans += tans;
    }
    cout<<ans<<endl;

    return 0;
}

你可能感兴趣的:(动态规划,树,NOI,ZJOI,独立集)