zoukankan      html  css  js  c++  java
  • Luogu P2607 骑士

    这是个基环数(森林) DP

    题面 
    首先读完题,我们思考一下他都有什么性质:
    1.乍一看有点像树形背包,因为他在选择物品的时候有依赖性,即选择某件物品之后,与之对应的某件物品无法使用
    3.显然,一般一道题目没有明显说明“无环”的情况,肯定会有环的各种各样数据产生。
    4.我认为也是最重要的,做题的话要尽可能地思索能得到什么,或者说你能想到解决问题的过程当中有什么困难,显然这个题"环”的处理不太好像。我们在建树的过程中是将自己厌恶的骑士当成自己的父亲(绝了),那么像构成环的话,每个点的出度都给了自己的父亲,那怎么才能出现环呢?谁的出度不会给父亲,只有根节点,所以一棵基环数有且仅有一个简单环且根节点必定在换之内。
    然后我们可以开始想如何DP,显然,思考进行当前这一步选择的时候什么会影响到我,我会影响到什么,我选了,有的人就不能选,有的人选了,我就不能选,很明显的我“选不选”实在转移时必须要知道的条件,即状态f[i][1/0]表示以i为子树,当前i选/不选的最大值。
    方程也很简单,模板
    f[i][0] +=max(f[son][0],f[son][1]);
    f[i][1] +=f[son][0] //不加当前的价值因为习惯性赋值f[now][1] = val[now]
     
    其实说白了这道题就是没有上司的舞会 + 环的处理
    怎么树形Dp呢,在找到一个环之后就把他拆成一棵树,随便删一条边即可,删的那条边的两个点就可以进行DP,然后肯定不能真正的修改图,可以强制选择其中一个点,做一次,在强制选择另一个点做一次。
    剩下统计一下答案就输出了,思路非常清晰。
    DP : think twice, code once

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    //DP 之前先明确:自己的任务有哪些
    //1.找到每一棵基环数的环,随意的拆开
    //2.做两遍树形Dp求Max.
    //小点:判断当前这是棵树,和森林区分开.
    //对于每一个节点,都需要看看根据他为根节点是不是一棵基环树,不是其实最好
    
    int head[N], cnt;
    int n, val[N], inb;
    LL ans, f[N][2];
    int fa[N], v;
    int fe, fw, fc, fv;//任意挑的两个和判断是否是基环数
    bool vis[N];
    
    struct Edge{
        int to;
        int nxt;
    }e[N << 1];
    
    
    void add(int x,int y){
        e[++cnt] = (Edge){y, head[x]}, head[x] = cnt;
    }
    
    void dfs(int x, int fath){
        vis[x] = true;
        for(int i = head[x];i;i = e[i].nxt){
            v = e[i].to;
            if(v == fath) continue;
            if(!vis[v])
                dfs(v, x);
            else if(vis[v] && !fv)
                fv = true, fc = i, fe = x, fw = v;
        }
    }
    
    
    void dp(int u, int fa) {
        f[u][0] = 0, f[u][1] = val[u];
        for (int i = head[u], v; i; i = e[i].nxt) 
            if ((v = e[i].to) != fa && i != fc && i != (fc ^ 1)) {
                dp(v, u);
                f[u][0] += max(f[v][0], f[v][1]);
                f[u][1] += f[v][0];
            }
    }
    
    int main(){ 
        cnt = 1;
        scanf("%d", &n);
        for(int i = 1;i <= n;++ i){
            scanf("%d %d", &val[i], &inb);
            add(i, inb);//正常建边
            add(inb, i);
        }
        for(int i = 1;i <= n;++ i){//每个点扫一遍
            if(!vis[i]){
                fv = false, fc = fe = fw = 0;
                dfs(i, 0);
                if(fv) {//有环
                    dp(fw, 0);
                    LL tmp = f[fw][0];
                    dp(fe, 0);
                    tmp = max(tmp, f[fe][0]);
                    ans += tmp;
                }
                else {
                    dp(i, 0);
                    ans += max(f[i][0], f[i][1]);
                }
            }
        }
        printf("%lld", ans);
        return 0;
    }
  • 相关阅读:
    ASP.NET中Cookie编程的基础知识
    一道编程题
    软件开发一点心得
    迅雷产品经理笔试题
    常用JS 1
    设计模式
    整理思路
    抽象工厂模式 Abstract Factory
    单件模式(Single Pattern)
    序列化
  • 原文地址:https://www.cnblogs.com/Shu-Kuang/p/12783149.html
Copyright © 2011-2022 走看看