树形 dp 专题

题单

小G有一个大树

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    while (cin >> n) {
        vector<vector<int>> e(n + 1);
        for (int i = 1, x, y; i < n; i++)
            cin >> x >> y, e[x].push_back(y), e[y].push_back(x);
        vector<int> tot(n + 1), fa(n + 1);
        fa[1] = -1;


        auto dfs = [&](auto &&self, int x) -> void {
            tot[x] = 1;
            for (auto y: e[x]) {
                if (y == fa[x]) continue;
                fa[y] = x;
                self(self, y);
                tot[x] += tot[y];
            }
            
        };

        dfs(dfs, 1);
        int res = LLONG_MAX , id = -1 ;
        for( int x = 1 , ans ; x <= n ; x ++ ){
            ans = n - tot[x];
            for( auto y : e[x] ){
                if( y == fa[x] ) continue;
                ans = max( ans , tot[y] );
            }
            if( ans < res ) res = ans , id = x;
        }
        cout << id << " " << res << "\n";
    }
    return 0;
}

没有上司的舞会

最大独立集,一条边的两个端点只能选一个,问最多选多少个点。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    vector<int> h(n + 1);
    for (int i = 1; i <= n; i++) cin >> h[i];
    vector<vector<int>> e(n + 1);
    int root = n * (n + 1) / 2;
    for (int i = 1, x, y; i < n; i++)
        cin >> y >> x, e[x].push_back(y), root -= y;
    vector<array<int, 2>> f(n+1);
    auto dfs = [e, h, &f](auto &&self, int x) -> void {
        f[x][1] = h[x];
        for (auto y: e[x]) {
            self(self, y);
            f[x][0] += max(f[y][0], f[y][1]);
            f[x][1] += f[y][0];
        }
        return;
    };
    dfs(dfs, root);
    cout << max(f[root][1], f[root][0]);
    return 0;
}

Strategic game

最小点覆盖,选一个点可以把相邻的边覆盖,问最少选多少个点可以把所有的边覆盖。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    int n;
    while (cin >> n) {
        vector<vector<int>> e(n);
        vector<array<int, 2>> f(n);
        int root = n * (n - 1) / 2;
        for (int x, y, t, i = 1; i <= n; i++)
            for( x = read() , t = read() ; t ; t -- )
                y = read(), e[x].push_back(y), root -= y;
        auto dfs = [e, &f](auto &&self, int x) -> void {
            f[x][1] = 1;
            for (auto y: e[x]) {
                self(self, y);
                f[x][1] += min(f[y][0], f[y][1]);
                f[x][0] += f[y][1];
            }
        };
        dfs(dfs, root);
        cout << min(f[root][1], f[root][0]) << "\n";
    }
    return 0;
}

Cell Phone Network

最小支配集,选一个点可以把相邻的点覆盖,问最少选多少个点可以把所有的点覆盖。

#include <bits/stdc++.h>

using namespace std;

constexpr int inf = 1e9;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    vector<vector<int>> e(n + 1);
    for (int i = 1, x, y; i < n; i++)
        cin >> x >> y, e[x].push_back(y), e[y].push_back(x);
    vector<array<int, 3>> f(n + 1);
    // f[x][0] x 被自己覆盖
    // f[x][1] x 被儿子覆盖
    // f[x][2] x 被父亲覆盖

    auto dfs = [&f, e](auto &&self, int x, int fa) -> void {
        f[x][0] = 1;
        f[x][1] = inf;
        f[x][2] = 0;
        int inc = inf;
        for (auto y: e[x]) {
            if (fa == y) continue;
            if (f[x][1] == inf) f[x][1] = 0;
            self(self, y, x);
            f[x][0] += min({f[y][0], f[y][1], f[y][2]});
            f[x][2] += min(f[y][0], f[y][1]);
            f[x][1] += min(f[y][0], f[y][1]), inc = min(inc, f[y][0] - f[y][1]);
        }
        f[x][1] += max(0, inc);
        return;
    };

    dfs(dfs, 1, -1);
    cout << min(f[1][0], f[1][1]);
    return 0;
}

二叉苹果树

#include <bits/stdc++.h>

using namespace std;


constexpr int inf = 1e9;

typedef pair<int, int> pii;


int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n, m;
    cin >> n >> m;
    vector<vector<pii>> e(n + 1);
    for (int i = 1, x, y, z; i < n; i++)
        cin >> x >> y >> z , e[x].emplace_back(y, z), e[y].emplace_back(x, z);
    vector<vector<int>> g(n + 1);
    vector<int> val(n + 1);
    auto dfs = [e, &g, &val](auto &&self, int x, int fa) -> void {
        for (auto [y, w]: e[x]) {
            if (y == fa) continue;
            g[x].push_back(y), val[y] = w;
            self(self, y, x);
        }
    };
    dfs(dfs, 1, -1);
    vector<vector<int>> f(n + 1, vector<int>(m + 1, -inf));
    auto dp = [g, val, &f](auto &&self, int x, int t) -> int {
        if (f[x][t] != -inf) return f[x][t];
        if (t == 0) return f[x][t] = 0;
        if (g[x].empty()) return f[x][t] = 1 - inf;
        int l = g[x][0], r = g[x][1];
        f[x][t] = max(self(self, l, t - 1) + val[l], self(self, r, t - 1) + val[r]);
        for (int j = 0; j <= t - 2; j++)
            f[x][t] = max(f[x][t], self(self, l, j) + self(self, r, t - 2 - j) + val[l] + val[r]);
        return f[x][t];
    };
    cout << dp(dp, 1, m);
    return 0;
}

树上子链

两边 dfs 求直径

#include<bits/stdc++.h>

using namespace std;

#define mp make_pair

#define int long long

int res = LLONG_MIN;
vector<int> v, f;
vector<vector<int>> e;

void dfs(int x, int fa) {
    f[x] = v[x];
    for (auto y: e[x]) {
        if (y == fa) continue;
        dfs(y, x);
        res = max(res, f[x] + f[y]);
        f[x] = max(f[x], f[y] + v[x]);
    }
    res = max(res, f[x]);
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    v.resize(n + 1);
    for (int i = 1; i <= n; i++) cin >> v[i];
    e.resize(n + 1);
    for (int i = 1, x, y; i < n; i++)
        cin >> x >> y, e[x].push_back(y), e[y].push_back(x);
    f.resize(n + 1);
    dfs(1, -1);
    cout << res << "\n";
    return 0;
}

树形 DP

#include <bits/stdc++.h>

using namespace std;

#define int long long

constexpr int inf = 1e18;

typedef pair<int, int> pii;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    vector<int> val(n + 1);
    vector<vector<int>> e(n + 1);
    for (int i = 1; i <= n; i++) cin >> val[i];
    for (int i = 1, x, y; i < n; i++)
        cin >> x >> y, e[x].push_back(y), e[y].push_back(x);
    vector<int> f(n + 1 , - inf ), dis(n + 1);

    auto dfs = [e, val, &f, &dis](auto &&self, int x, int fa) -> void {
        multiset<int> t;
        for (auto y: e[x]) {
            if (y == fa) continue;
            self(self, y, x);
            f[x] = max(f[x], f[y]);
            dis[x] = max(dis[x], dis[y] + val[y]);
            t.insert(dis[y] + val[y]);
            if (t.size() == 3) t.erase(t.begin());
        }
        int w = val[x];
        for (auto i: t) w += i;
        f[x] = max(f[x], w);
        return;
    };
    dfs(dfs, 1, -1);
    cout << f[1];
    return 0;
}

Rinne Loves Edges

\(f[x]\)表示\(x\)的子树上所以叶子与根断开的最小代价。

\[f[x]=\sum \min( f[y] , w) \]

其中\(y\)表示\(x\)的儿子,\(w\)表示\(x,y\)之间边的边权。

#include <bits/stdc++.h>

using namespace std;

#define int long long

using pii = pair<int, int>;


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, s;
    cin >> n >> m >> s;
    vector<vector<pii>> e(n + 1);
    for (int i = 1, x, y, z; i <= m; i++)
        cin >> x >> y >> z, e[x].emplace_back(y, z), e[y].emplace_back(x, z);

    vector<int> f(n + 1, LLONG_MAX);
    auto dp = [&f, e](auto &&self, int u, int fa) -> int {
        if (f[u] != LLONG_MAX) return f[u];
        if (e[u].size() == 1 and e[u].front().first == fa) return --f[u];
        f[u] = 0;
        for (auto [v, w]: e[u])
            if( v != fa ) f[u] += min(self(self, v, u), w);
        return f[u];
    };
    cout << dp(dp, s, -1);
    return 0;
}

吉吉王国

与上一题基本相同,在加一个二分答案,删边的过程中只删长度小于\(mid\)的即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

using pii = pair<int, int>;

constexpr int inf = 1e9;


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<pii>> e(n + 1), g(n + 1);
    for (int i = 1, u, v, w; i < n; i++)
        cin >> u >> v >> w, e[u].emplace_back(v, w), e[v].emplace_back(u, w);
    auto dfs = [e, &g](auto self, int u, int fa) -> void {
        for (const auto &[v, w]: e[u])
            if (v != fa) g[u].emplace_back(v, w), self(self, v, u);
    };
    dfs(dfs, 1, -1);
    int l = 0, r = 1e3, res = -1;
    for (int mid; l <= r;) {
        mid = (l + r) >> 1;
        vector<int> f(n + 1, inf);
        auto dp = [g, &f, mid](auto self, int u) {
            if (f[u] != inf) return f[u];
            if (g[u].empty()) return --f[u];
            f[u] = 0;
            for (const auto [v, w]: g[u]) {
                if (w <= mid) f[u] += min(self(self, v), w);
                else f[u] += self(self, v);
            }
            return f[u];
        };
        if( dp(dp,1) <= m ) res = mid , r = mid - 1;
        else l = mid + 1;
    }
    cout << res << "\n";

    return 0;
}
posted @ 2023-09-12 21:36  PHarr  阅读(8)  评论(0编辑  收藏  举报