2.20

2.20

[P1040 NOIP 2003 提高组] 加分二叉树 - 洛谷 (luogu.com.cn)

  • 首先说明这是一道区间dp
  • 我们可以定义\(dp[i,j]\)i结点到j结点为这根树的最大分数,那么可以考虑就是\([i,j]\)区间中存在k为根节点
  • 迭代方程就为\(dp[i][j] = max(dp[i][j],dp[i][k - 1] * dp[k + 1][j] + d[k])\),注意k的取值范围
  • ok,考虑到区间dp,我们迭代方向肯定就是从小往大迭代
  • 然后就是初始状态了,我们考虑就是如果\(dp[i][i]\)也就是叶子结点,那么此刻值就为本身,然后\(dp[i][i+1]\)长度为2,其实值都一样,就是相加因为谁×空子树1都没意义
  • 至于我们根节点,维护一个\(root[i][j]\)来存这个区间根节点即可,也需要初始化详细看代码
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 = 35;
    static int[] d = new int[N];
    static int[][] dp = new int[N][N];
    static int[][] root = new int[N][N];
    static 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 left, int right) {
        if (left > right) return;
        int root_ = root[left][right];
        cout.print(root_ + " ");
        dfs(left, root_ - 1);
        dfs(root_ + 1, right);
    }



    @Override
    public void run() {
        try {
            n = nextInt();
            for (int i = 1; i <= n; i++) {
                d[i] = nextInt();
                dp[i][i] = d[i];
                root[i][i] = i;
            }

            for (int i = 1; i < n; i++) {
                dp[i][i + 1] = d[i] + d[i + 1];
                root[i][i + 1] = i;
            }

            for (int len = 3; len <= n; len++) {
                for (int left = 1; left + len - 1 <= n; left++) {
                    int right = left + len - 1;
                    for (int k = left + 1; k < right; k++) {
                        if (dp[left][k - 1] * dp[k + 1][right] + d[k] > dp[left][right]) {
                            dp[left][right] = dp[left][k - 1] * dp[k + 1][right] + d[k];
                            root[left][right] = k;
                        }
                    }
                }
            }

            cout.println(dp[1][n]);
            dfs(1, n);

            cout.flush();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

P1122 最大子树和 - 洛谷 (luogu.com.cn)

  • 我们定义\(dp[i]\)为以i为根节点的子树分数最大的值,相当于他只能存其根节点的值或者加上子节点权值
  • 考虑到可能子节点权值为负,越加肯定越小。那么我们dfs到叶子结点时,如果它的值为负,那么它对我们没有意义,就不加即可,所以推理出转移方程\(dp[parent] += dp[son] + btf[parent](dp[son] > 0)\)
  • 初始状态就dfs下去时直接设置即可
  • 其实我当时觉得我们dp数组意义是不太好的,因为我们不能确定以i结点为根结点的值是否为最大和。事实确实如此例如样例中\(dp[5] = 1\),如果我们用1开是dfs的话,我就想如果从每个点都dfs一下,但是这肯定会TLE
  • 随后我想到了连通图的性质,其实我们最后保留的树就是个连通图,连通图的权值和一定是一定的,应该说如果我们从连通图里的点为根进行dfs,我们根节点的dp值就是我们所求值,这里我们并不知道这个点,其实也不需要知道,我们只要随机选个点为根节点进行搜索,我们一定会搜索到连通图,而连通图连接到外面非连通图的点,总有一个点可以为连通图根结点,那么这个点的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 = (int) (16e3 + 10);
    static Vector<Integer>[] nodes = new Vector[N];
    static int n;
    static int[] btf = new int[N];
    static int[] dp = 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 root, int pre) {

        dp[root] = btf[root];
        for (Integer son : nodes[root]) {
            if (son == pre) continue;
            dfs(son, root);
            if (dp[son] > 0) {
                dp[root] += dp[son];
            }
        }
    }



    @Override
    public void run() {
        try {
            n = nextInt();
            for (int i = 1; i <= n; i++) nodes[i] = new Vector<>();
            for (int i = 1; i <= n; i++) {
                btf[i] = nextInt();
            }

            for (int i = 1; i <= n - 1; i++) {
                int a = nextInt(); int b = nextInt();
                nodes[a].add(b);nodes[b].add(a);
            }

            dfs(1, -1);

            int ans = -0x3f3f3f3f;
            for (int i = 1; i <= n; i++) {
                ans = Math.max(ans, dp[i]);
            }
            cout.println(ans);
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

P2016 战略游戏 - 洛谷 (luogu.com.cn)

  • 简单的树形dp,我们定义\(dp[i][1/0]\)为i结点选或者不选时士兵最少数,如果选,那么字节点选不选也没有意义,即\(dp[parent][1] += min(dp[son][1], dp[son][0])\),如果不选儿子结点就成了必选,\(dp[parent][0] += dp[son][1]\)
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) (15e2 + 10);
    static Vector<Integer>[] nodes = new Vector[N];
    static int 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, int pre) {
        dp[root][1] = 1;
        dp[root][0] = 0;

        for (Integer son : nodes[root]) {
            if (pre == son) continue;
            dfs(son, root);
            dp[root][1] += Math.min(dp[son][0], dp[son][1]);
            dp[root][0] += dp[son][1];
        }
    }



    @Override
    public void run() {
        try {
            n = nextInt();
            for (int i = 0; i <= n - 1; i++) nodes[i] = new Vector<>();
            for (int i = 1; i <= n; i++) {
                int x = nextInt(); int k = nextInt();
                for (int j = 1; j <= k; j++) {
                    int y = nextInt();
                    nodes[x].add(y);
                    nodes[y].add(x);
                }
            }

            for (int i = 0; i <= n - 1; i++) Arrays.fill(dp[i], 0x3f3f3f3f);

            dfs(0, -1);

            cout.println(Math.min(dp[0][1], dp[0][0]));
            cout.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


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