2.17

2.17

P1352 没有上司的舞会 - 洛谷 (luogu.com.cn)

  • \(dp[i][1/0]\)表示以i结点为根节点时,选或者不选所得到最大幸福值

  • 我们dfs到叶子结点后在回溯时进行dp

  • 假如我们选择了boss,那么boss的子节点就不能再选,\(dp[parent][1] += dp[son][0]\),而没有选择时,有两种可能,\(dp[parent][0] = Math.max(dp[son][1], dp[son][0])\)

import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;

public class Main implements Runnable {

    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    static final int N = (int) (6e3 + 10);
    static Vector<Integer>[] mans = new Vector[N];
    static int n;
    static int[] happy = new int[N];
    static boolean[] hasFather = new boolean[N];
    static int[][] dp = new int[N][2];

    public static void main(String[] args) {
        new Thread(null, new Main(), "", 1 << 29).start();
    }

    public static int nextInt() throws IOException {
        cin.nextToken();
        return (int) cin.nval;

    }

    public static void dfs(int root) {
        dp[root][1] = happy[root];
        for (Integer son : mans[root]) {
            dfs(son);
            dp[root][1] += dp[son][0];
            dp[root][0] += Math.max(dp[son][1], dp[son][0]);
        }
    }

    @Override
    public void run() {
        try {
            n = nextInt();
            for (int i = 1; i <= n; i++) {
                happy[i] = nextInt();
                mans[i] = new Vector<>();
            }
            for (int i = 1; i <= n - 1; i++) {
                int l = nextInt(); int k = nextInt();
                mans[k].add(l);
                hasFather[l] = true;
            }
            int root = 0;
            for (int i = 1; i <= n; i++) {
                if (!hasFather[i]) {
                    root = i;
                    break;
                }
            }
            dfs(root);
            cout.println(Math.max(dp[root][1], dp[root][0]));
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

[P2014 CTSC1997] 选课 - 洛谷 (luogu.com.cn)

  • 其实是一个背包问题
  • 定义\(dp[i][j]\)为以i结点为根节点,容量为j的的最大得分,因为本题拥有前提条件,即只有我们在父结点选择后,子节点才能跟着选择,所以考虑组合背包
  • 把每一个结点下的子树看作一组背包,在保证父结点体积的情况下,增加子树容量,即\(dp[root][j] = max(dp[root][j], dp[son][0],dp[son][1],dp[son][2].....dp[son][j - weight[root]])\),所以我们只需要遍历容量j和k(\(0<=k<=j-weight[root]\)
  • dp的原则就是将一个大的问题拆分为无数的小问题,再将小问题得到的结果迭代成大结果
  • 刚开始还在疑惑为什么可以这样做,其实就是如果我们要迭代到最高的点,我们肯定需要子节点的答案,那么直到到达叶子结点后,我们才能层层返回答案给叶子结点
  • 记得在dfs搜索到叶子结点时赋初值,注意在我们dfs下去时就对dp就行初始化
  • 注意的是我们得到的其实是森林,理想状态下我们需要把他转为一棵树,那么我们只要添加一个结点来成为这片森林的根节点的父结点即可
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;

public class Main implements Runnable {

    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    static final int N = 310;
    static Vector<Integer>[] node = new Vector[N];
    static int[] val = new int[N];
    static int n, m;
    static int[][] dp = new int[N][N];// 以i结点为根节点,容量为j的的最大得分
    static int[] v = new int[N];

    public static void main(String[] args) {
        new Thread(null, new Main(), "", 1 << 29).start();
    }

    public static int nextInt() throws IOException {
        cin.nextToken();
        return (int) cin.nval;

    }

    public static void dfs(int parent) {
        for (int i = 1; i <= m; i++) {
            dp[parent][i] = val[parent];
        }
        for (Integer son : node[parent]) {
            dfs(son);
            for (int j = m; j >= v[parent]; j--) {
                for (int k = 0; k <= j - v[parent]; k++) {
                    dp[parent][j] = Math.max(dp[parent][j], dp[son][k] + dp[parent][j - k]);
                }
            }
        }
    }

    @Override
    public void run() {
        try {
           n = nextInt();m = nextInt();
           for (int i = 0; i <= n; i++) node[i] = new Vector<>();
           for (int i = 1; i <= n; i++) {
               int parent = nextInt();
               val[i] = nextInt();v[i] = 1;
               node[parent].add(i);
           }
           dfs(0);
           cout.println(dp[0][m]);
           cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

P2015 二叉苹果树 - 洛谷 (luogu.com.cn)

  • 这个做法可能有点不同,题解基本是用边来作为背包容量,我是将边转为点数,考虑到一条边肯定连两个点,然后保留q条边,那么最后肯定有q+1个点
  • 由于点是小于100的,所以采用疏密图来存储边权也是ok的
  • 本题\(dp[i][j]\)为以i为根节点的子树在保留j个结点前的最大苹果数和
  • dfs赋值时为什么是加\(val[parent][pre]\)呢,因为我们在达到这个点时,是默认该点父结点是保留下来了的(不保留也就不可能到达当前结点),那么在这棵树中也一定保留了当前的这个边,所以先加上方便后续迭代
  • 后续dp就和上一题一个思路了
  • 其实应该要用数学公式来详细证明这个代码的可靠性,但是笔者实力有限,不太会了......
  • 感觉有点投机取巧了这个题
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;

public class Main implements Runnable {

    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    static final int N = 110;
    static Vector<Integer>[] node = new Vector[N];
    static int n, q, m;
    static int[][] val = new int[N][N];
    static int[][] dp = new int[N][N];

    public static void main(String[] args) {
        new Thread(null, new Main(), "", 1 << 29).start();
    }

    public static int nextInt() throws IOException {
        cin.nextToken();
        return (int) cin.nval;

    }

    public static void dfs(int parent, int pre) {
        for (int i = 1; i <= m; i++) {
            dp[parent][i] = val[parent][pre];
        }
        for (Integer son : node[parent]) {
            if (son == pre) continue;
            dfs(son, parent);
            for (int j = m; j >= 1; j--) {
                for (int k = 0; k <= j - 1; k++) {
                    dp[parent][j] = Math.max(dp[parent][j], dp[son][k] + dp[parent][j - k]);
                }
            }
        }
    }

    @Override
    public void run() {
        try {
            n = nextInt();q = nextInt();
            for (int i = 1; i <= n; i++) node[i] = new Vector<>();
            for (int i = 1; i <= n - 1; i++) {
                int x = nextInt();int y = nextInt(); int v = nextInt();
                node[x].add(y);node[y].add(x);
                val[x][y] = v;val[y][x] = v;
            }
            m = q + 1;
            dfs(1, -1);
            cout.println(dp[1][m]);
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
posted @ 2025-02-17 23:02  Mikkeykarl  阅读(14)  评论(0)    收藏  举报