[ZJOI2008] 骑士(拆环,搜索)

Posted markun0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2008] 骑士(拆环,搜索)相关的知识,希望对你有一定的参考价值。

题意:

有个n个骑士,每个骑士都有且仅有一个自己最厌恶的骑士,现在需要选出一批骑士,要求每个骑士在其中都不会碰上自己最厌恶的骑士,请输出能有的最大战力和

思路:

该题和没有上司的舞会非常相似,可以逆序查找讨厌当前骑士的所有骑士,靠构造带根节点的数来求出每个连通块的最大战力

遇到的问题: 环

解决:可以发现每个连通块有且仅有一个环,通过拆环可以把连通块构造成想要的树形

代码如下:

#include <iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll n, a[N], x, y, father[N], dp[N][2],sum,vist[N];
vector<int>son[N];
void dfs(int k,int x) 
    //标记连通块上所有点
    vist[k] = 1;
    dp[k][1] = a[k];
    for (int i = 0; i < son[k].size(); i++) 
        int u = son[k][i];
        //环被拆开的位置
        if (x == u)continue;
        dfs(u,x);
        dp[k][1] += dp[u][0];
        dp[k][0] += max(dp[u][1], dp[u][0]);
        //使用后顺带清理数据
        dp[u][0] = 0; dp[u][1] = 0;
    

//找环
void dfs0(int k) 
    //给进过的点做记号
    vist[k] = 1;
    int u = father[k];
    //找到对应环
    if (vist[u]) 
        //分别以u,k为根节点建树,连通块的最大战力为max(dp[u][0],dp[k][0])
        dfs(k, k); x = dp[k][0];
        dp[k][0] = 0; dp[k][1] = 0;
        dfs(u, u); y = dp[u][0];
        dp[u][0] = 0; dp[u][1] = 0;
        sum+=max(x,y);
    
    else
    dfs0(u);

int main()

    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++) 
        cin >> a[i] >> y;
        father[i] = y;
        //靠逆向查找建树
        son[y].push_back(i);
    
    for (int i = 1; i <= n; i++) 
        if(vist[i]==0)dfs0(i);
    
    cout << sum << endl;
    return 0;

[ZJOI2008]骑士

1040: [ZJOI2008]骑士

Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 5056  Solved: 1935 [Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3
10 2
20 3
30 1

Sample Output

30

HINT

N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。

题意为在基环树上选点,相邻的两个点不能同时选,求能选出的最大点权
如果是在树上做,设$f[i][0]$表示$i$不选,以$i$为根的子树中能选出的最大值,$f[i][1]$表示$i$选,以$i$为根的子树中能选出的最大值,转移很显然
基环树可以选择拆环,删掉环上的任意一条边,然后枚举两边的点哪个不取,dp即可
#include <cstdio>
#include <algorithm>
using namespace std;
inline int readint(){
    int n = 0;
    char ch = getchar();
    while(ch < 0 || ch > 9) ch = getchar();
    while(ch <= 9 && ch >= 0){
        n = (n << 1) + (n << 3) + ch - 0;
        ch = getchar();
    }
    return n;
}
const int maxn = 1000000 + 10;
struct Edge{
    int to, next;
    bool ok;
    Edge(){}
    Edge(int _t, int _n): to(_t), next(_n), ok(true){}
}e[maxn << 1];
int fir[maxn] = {0}, cnt = 1;
inline void ins(int u, int v){
    e[++cnt] = Edge(v, fir[u]); fir[u] = cnt;
    e[++cnt] = Edge(u, fir[v]); fir[v] = cnt;
}
int val[maxn], vis[maxn] = {0};
int rt1, rt2;
long long f[maxn][2];
void dp(int u, int T){
    vis[u] = T;
    f[u][0] = 0;
    f[u][1] = val[u];
    for(int v, i = fir[u]; i; i = e[i].next){
        v = e[i].to;
        if(!e[i].ok || vis[v] == T) continue;
        dp(v, T);
        f[u][0] += max(f[v][0], f[v][1]);
        f[u][1] += f[v][0];
    }
}
void dfs(int u, int f){
    vis[u] = 1;
    for(int v, i = fir[u]; i; i = e[i].next){
        v = e[i].to;
        if(i ^ f){
            if(vis[v]){
                rt1 = u;
                rt2 = v;
                e[i].ok = false;
                e[i ^ 1].ok = false;
            }
            else dfs(v, i ^ 1);
        }
    }
}
int main(){
    int n = readint();
    for(int i = 1; i <= n; i++){
        val[i] = readint();
        ins(i, readint());
    }
    long long ans = 0, t;
    for(int i = 1; i <= n; i++)
        if(!vis[i]){
            rt1 = rt2 = 0;
            dfs(i, 0);
            if(!rt1){
                dp(i, 2);
                ans += max(f[i][0], f[i][1]);
            }
            else{
                dp(rt1, 2);
                t = f[rt1][0];
                dp(rt2, 3);
                t = max(t, f[rt2][0]);
                ans += t;
            }
        }
    printf("%lld\n", ans);
    return 0;
}

 

以上是关于[ZJOI2008] 骑士(拆环,搜索)的主要内容,如果未能解决你的问题,请参考以下文章

[ZJOI2008]骑士

BZOJ 1040: [ZJOI2008]骑士

bzoj1040[ZJOI2008]骑士

BZOJ1040: [ZJOI2008]骑士

ZJOI2008骑士[树形dp]

bzoj 1040 1040: [ZJOI2008]骑士