• [ZJOI2008]骑士


    题目描述

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

    输入输出格式

    输入格式:
    输入文件knight.in第一行包含一个正整数N,描述骑士团的人数。
    接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。
    输出格式:
    输出文件knight.out应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
    输入输出样例
    输入样例#1:
    3
    10 2
    20 3
    30 1
    输出样例#1:
    30
    说明
    对于30%的测试数据,满足N ≤ 10;
    对于60%的测试数据,满足N ≤ 100;
    对于80%的测试数据,满足N ≤ 10 000。
    对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。


    题解

    这是一道基环树DP题,本蒟蒻也是看了题解才打出来的
    基环树???
    是不是看了基环树这个名词一脸懵逼

    • 首先什么是基环树,我的理解为:删去一条边是一棵树的图
      由于题目告诉我们:骑士都有且仅有一个自己最厌恶的骑士,所以你会发现连边后,形成一个基环树森林。
    • 如果这是一个树,那么我们可以直接DP求解,f[u][0] += max(f[v][1], f[v][0]);
      f[u][1] += f[v][0]; u,v之间有一条边,0表示不选这个点,1表示选。
    • 然后如果我们把基环树删去环的一条边,那它就变成一颗树了,所以整个思路就是,找到每一个环,删除其中一条边,两个点u, v分别进行一次DP,强制不一起选这两个点,每一个基环树的答案就为max(max(f[u][0], f[u][1]), max(f[v][0], f[v][1])),最后把所有基环树的答案相加即可。

    常数巨大的丑陋代码

    # include <stdio.h>
    # include <stdlib.h>
    # include <iostream>
    # include <string.h>
    # include <math.h>
    using namespace std;
    
    # define IL inline
    # define RG register
    # define UN unsigned
    # define ll long long
    # define rep(i, a, b) for(RG int i = a; i <= b; i++)
    # define per(i, a, b) for(RG int i = b; i >= a; i--)
    # define uev(e, u) for(RG int e = ft[u]; e != -1; e = edge[e].nt)
    # define mem(a, b) memset(a, b, sizeof(a))
    # define max(a, b) ((a) > (b)) ? (a) : (b)
    # define min(a, b) ((a) < (b)) ? (a) : (b)
    
    IL ll Get(){
        RG char c = '!'; RG ll num = 0, z = 1;
        while(c != '-' && (c > '9' || c < '0')) c = getchar();
        if(c == '-') z = -1, c = getchar();
        while(c >= '0' && c <= '9') num = num*10+c-'0', c = getchar();
        return num * z;
    }
    
    const int MAXN = 1000001;
    struct Edge{
        int to, nt;
    } edge[MAXN << 1];
    int n, cnt, ft[MAXN], vis[MAXN], w[MAXN], cutu, cutv, pos;
    ll f[MAXN][2], ans;
    
    IL void Add(RG int u, RG int v){
        edge[cnt] = (Edge){v, ft[u]}; ft[u] = cnt++;
    }
    
    IL void Dfs(RG int u, RG int fa){
        vis[u] = 1;
        uev(e, u){
            RG int v = edge[e].to;
            if(v == fa) continue;
            if(!vis[v]) Dfs(v, u);
            else cutu = u, cutv = v, pos = e;
        }
    }
    
    IL void DP(RG int u, RG int fa, RG int cv){
        f[u][0] = f[u][1] = 0;
        if(u != cv) f[u][1] = w[u];
        uev(e, u){
            RG int v = edge[e].to;
            if(v == fa || e == pos || e == (pos^1)) continue;
            DP(v, u, cv);
            f[u][1] += f[v][0];
            f[u][0] += max(f[v][0], f[v][1]);
        }
    }
    
    int main(){
        mem(ft, -1);
        n = Get();
        rep(i, 1, n){
            w[i] = Get();
            RG int v = Get();
            Add(i, v); Add(v, i);
        }
        rep(i, 1, n){
            if(vis[i]) continue;
            Dfs(i, 0);
            DP(cutu, 0, cutv);
            RG ll t = max(f[cutu][0], f[cutu][1]);
            DP(cutv, 0, cutu);
            ans += max(t, max(f[cutv][0], f[cutv][1]));
        }
        printf("%lld", ans);
        return 0;
    }
  • 相关阅读:
    一些特殊的矩阵快速幂 hdu5950 hdu3369 hdu 3483
    HDU
    UVA-796 Critical Links 找桥
    HDU 4612 Warm up
    2017 ICPC乌鲁木齐 A Coins 概率dp
    HDU6223 Infinite Fraction Path bfs+剪枝
    Java基础知识学习(一)
    算法(一)
    面试题整理:SQL(二)
    面试题整理:SQL(一)
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8206413.html
Copyright © 2020-2023  润新知