时间:
分类: Algo

关于这段时间尝试巴结树形 DP 这件事.jpg

感悟

暂无.jpg

题目

可能更多的是代码记录罢了.jpg

P1272 重建道路

依旧是看了半天题解才看明白,但是题解几种做法不太一样,但是转移方程是差不多的。

还是太弱了.jpg

代码:

#include <iostream>
#include <vector>

int n, p;
std::vector<std::vector<int>> e; // 存图
std::vector<std::vector<int>> f; // f[i][j]表示以 i 为根保留 j 个 节点所需要删掉的边的最小数量
std::vector<int> deg; // 出度计数

int dfs(int u) {
    int sum = 1, siz; // 自己也算一个节点
    for (int v : e[u]) {
        siz = dfs(v);
        sum += siz;
        for (int j = sum; j >= 1; j--) {
            for (int k = 1; k < j; k++) {
                f[u][j] = std::min(f[u][j], f[u][j - k] + f[v][k] - 1); // 减掉的是 u 与 v 相连的那条边
            }
        }
    }
    return sum;
}

int main() {
    std::cin >> n >> p;
    e.resize(n + 1);
    f.resize(n + 1, std::vector<int>(n + 1, 0x3f3f3f3f));
    deg.resize(n + 1);

    for (int i = 0; i < n - 1; i++) {
        int u, v;
        std::cin >> u >> v;
        deg[u]++; // 子节点数增加,出度
        e[u].emplace_back(v);
    }
    for (int i = 1; i <= n; i++) {
        f[i][0] = 0; // 保留 0 自然是 0
        f[i][1] = deg[i]; // 保留 1 就是子节点数
    }
    dfs(1);

    int ans = f[1][p];
    for (int i = 1; i <= n; i++) {
        if (ans > f[i][p]) ans = f[i][p] + 1; // 除 1 以外,保留一棵子树还需要断掉 u 与 parent[u] 相连的那条边
    }

    std::cout << ans << '\n';
    return 0;
}

P1273 有线电视网

感觉比上一题好理解诶,也可能这题我前一天写过的原因(。

代码:

#include <iostream>
#include <vector>

const int INF = 0x3f3f3f3f;

int n, m;
std::vector<std::vector<std::pair<int, int>>> e;
std::vector<std::vector<int>> f; // f[i][j] 表示以 i 为根服务 j 个用户所获得的钱钱
std::vector<int> p; // 用户愿意交的钱钱

bool is_user(int u) { return u > n - m; } // 根据题意 u > n - m 就是用户

int dfs(int u) {
    if (is_user(u)) { 
        return 1; // 诶诶,这个是用户
    }
    int sum = 0, siz; // sum 为以 u 为根下的用户数量, siz 为以 v 为根下的用户数量
    for (auto [v, w] : e[u]) {
        siz = dfs(v);
        sum += siz;
        for (int j = sum; j >= 1; j--) {
            for (int k = 1; k <= j; k++) { // 一定是 k <= j,因为可以全部都在 v 为根的子树里面取
                f[u][j] = std::max(f[u][j], f[u][j - k] + f[v][k] - w); // 减去传输边需要的钱
            }
        }
    }
    return sum;
}

int main() {
    std::cin >> n >> m;
    e.resize(n + 1);
    p.resize(n + 1);
    f.resize(n + 1, std::vector<int>(n + 1, -INF));

    for (int u = 1; u <= n - m; u++) {  // 题意建图
        int siz, v, w;
        std::cin >> siz;
        while (siz--) {
            std::cin >> v >> w;
            e[u].push_back({ v, w });
        }
    }

    for (int i = n - m + 1; i <= n; i++) { // 读入用户愿意花的钱钱
        std::cin >> p[i]; 
    }

    for (int i = 1; i <= n; i++) { 
        f[i][0] = 0; // 不服务当然是 0
        if (is_user(i)) f[i][1] = p[i]; // 是用户的话 f[user][1] = p[user]
    }

    dfs(1);

    for (int i = n; i >= 1; i--) { // 尽可能多的用户,倒序枚举
        if (f[1][i] >= 0) { // 大于等于,不亏本
            std::cout << i << '\n';
            break;
        }
    }
    return 0;
}

P2014 选课

妈耶我为什么写过了还是写不出来我不理解,卡在以 0 为根节点和 m + 1 个选项那了emmmm

但是确实是很简单的,但是我是fw.jpg

代码:

#include <iostream>
#include <vector>

int n, m;
std::vector<std::vector<int>> e;
std::vector<std::vector<int>> f;

void dfs(int u) {
    for (int v : e[u]) {
        dfs(v);
        for (int j = m + 1; j >= 0; j--) {
            for (int k = 0; k < j; k++) { // parent[v] 必选
                f[u][j] = std::max(f[u][j], f[u][j - k] + f[v][k]);
            }
        }
    }
}

int main() {
    std::cin >> n >> m;
    e.resize(n + 1);
    f.resize(n + 1, std::vector<int>(m + 2));

    for (int v = 1; v <= n; v++) {
        int u, w;
        std::cin >> u >> w;
        e[u].push_back(v);
        f[v][1] = w;
    }
    dfs(0); // 以 0 作为根节点,所以要选 m + 1 个
    std::cout << f[0][m + 1] << '\n';
    return 0;
}

P2585 三色二叉树

根据题意写转移方程就好,相邻节点不能同色。

代码:

#include <iostream>
#include <string>
#include <array>
#include <algorithm>

enum Color { R, G, B }; // 对应三种颜色,红,绿,蓝

struct Node {
    Node *childl, *childr;
    std::array<int, 3> fn, fm; // fn[u][R/G/B] 将 u 染对应颜色绿色个数的最小值, fm 最大值
    Node () : childl(nullptr), childr(nullptr), fn({ 0, 0, 0 }), fm({ 0, 0, 0 }) {}; // 初始化
};

Node* build(std::string &s, Node *nul) {
    Node *curr = new Node();
    char t = s.back();
    s.pop_back();

    if (t == '1') { // 为了方便,只有左节点的用空节点补成完全二叉树
        curr->childl = build(s, nul);
        curr->childr = nul;
    } else if (t == '2') {
        curr->childl = build(s, nul);
        curr->childr = build(s, nul);
    }
    return curr;
}

void dfs(Node *curr, Node *nul) {
    if (curr->childl && curr->childl != nul) dfs(curr->childl, nul); // 不为 nullptr 并且不为空节点,进行遍历
    if (curr->childr && curr->childr != nul) dfs(curr->childr, nul); // 同上

    if (!curr->childl && !curr->childr) {
        curr->fn[G] = 1;
        curr->fm[G] = 1;
        return;
    }

    // 很好理解,根据题目意思三种颜色的转移
    curr->fn[G] = std::min(curr->childl->fn[R] + curr->childr->fn[B], curr->childl->fn[B] + curr->childr->fn[R]) + 1;
    curr->fn[R] = std::min(curr->childl->fn[G] + curr->childr->fn[B], curr->childl->fn[B] + curr->childr->fn[G]);
    curr->fn[B] = std::min(curr->childl->fn[G] + curr->childr->fn[R], curr->childl->fn[R] + curr->childr->fn[G]);

    curr->fm[G] = std::max(curr->childl->fm[R] + curr->childr->fm[B], curr->childl->fm[B] + curr->childr->fm[R]) + 1;
    curr->fm[R] = std::max(curr->childl->fm[G] + curr->childr->fm[B], curr->childl->fm[B] + curr->childr->fm[G]);
    curr->fm[B] = std::max(curr->childl->fm[G] + curr->childr->fm[R], curr->childl->fm[R] + curr->childr->fm[G]);
}

int main() {
    std::string s;
    std::cin >> s;
    std::reverse(s.begin(), s.end()); // 反转,后续用 pop_back

    Node *nul = new Node(); // 空节点
    Node *root = build(s, nul);
    dfs(root, nul);
    std::cout << std::max({ root->fm[R], root->fm[G], root->fm[B] }) << ' ' << std::min({ root->fn[R], root->fn[G], root->fn[B] }) << '\n';
    return 0;
}

标签: none

评论已关闭