首页 > 其他分享 >树形 dp 专题

树形 dp 专题

时间:2023-09-12 21:46:50浏览次数:28  
标签:vector 专题 int auto self cin dfs 树形 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;
}

标签:vector,专题,int,auto,self,cin,dfs,树形,dp
From: https://www.cnblogs.com/PHarr/p/17697883.html

相关文章

  • 【题解】DP选练(23.9.11 - 23.9.12)
    一些写过题解的题我就直接挂连接了。[NOIP2018提高组]货币系统题目描述:在网友的国度中共有\(n\)种不同面额的货币,第\(i\)种货币的面额为\(a[i]\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为\(n\)、面额数组为\(a[1..n]\)的货币系统记作\((n,a)\)。......
  • USB适配器应用芯片 国产GP232RL软硬件兼容替代FT232RL DPU02直接替代CP2102
    USB适配器,是英文UniversalSerialBus(通用串行总线)的缩写,而其中文简称为“通串线”,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术,移动PC由于没有电池,电源适配器对其尤为重要。今天来讲讲USB适配器的国产适用芯片。一、GP232RL,直接软硬件......
  • dp 选练(基础版)
    P5664题目描述:Emiya是个擅长做菜的高中生,他共掌握\(n\)种烹饪方法,且会使用\(m\)种主要食材做菜。为了方便叙述,我们对烹饪方法从\(1\simn\)编号,对主要食材从\(1\simm\)编号。Emiya做的每道菜都将使用恰好一种烹饪方法与恰好一种主要食材。更具体地,Emiya会做\(a_......
  • 24UDP协议/操作系统发展
    作业#作业内容:实现上传和下载电影写了一个,另外一个反过来就可以(代码参考day24代码)#思考1.上传的电影如何判断是否重复小白思想:校验电影名称是否存在正确思想:校验电影的md5值(核心是内容不是名称)2.上传的电影如何判断是否有毒提前对电影内容加......
  • 【愚公系列】2023年09月 WPF控件专题 Canvas控件详解
    (文章目录)前言WPF控件是WindowsPresentationFoundation(WPF)中的基本用户界面元素。它们是可视化对象,可以用来创建各种用户界面。WPF控件可以分为两类:原生控件和自定义控件。原生控件是由Microsoft提供的内置控件,如Button、TextBox、Label、ComboBox等。这些控件都是WPF中常见......
  • 基础设施SIG月度动态:「龙蜥大讲堂」基础设施系列专题分享完美收官,容器镜像构建 2.0 版
    基础设施SIG(OpenAnolisInfraSIG)目标:负责OpenAnolis社区基础设施工程平台的建设,包括官网、Bugzilla、Maillist、ABS、ANAS、CI门禁以及社区DevOps相关的研发工程系统。01SIG整体进展1.龙蜥大讲堂-基础设施系列专题分享完美收官,8月邀请了多位SIG核心贡献者分享了包括......
  • 技术解码 | GB28181/SIP/SDP 协议--EasyGBS国标GB28181平台国标视频技术SIP解析
    EasyGBS国标视频云服务是基于国标GB/T28181协议的视频能力平台,可实现的视频功能包括:实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。平台部署简单、可拓展性强,支持将接入的视频流进行全终端、全平台分发,分发的视频流包括RTSP、RTMP、FLV、HLS、WebRTC等......
  • 3分钟看懂NPDP| 超全版
    NPDP考试是由美国产品开发与管理协会(PDMA)发起的产品经理国际资格认证考试,在中国国内由外专局培训中心举办考试,考生在报名参加NPDP考试前,需要了解NPDP,下面为大家详细介绍,供大家参考。 什么是NPDP?产品经理国际资格认证(NPDP),由美国产品开发与管理协会(PDMA)所发起,是国际公认的唯......
  • 2023年9月NPDP产品经理国际认证报名哪靠谱些?
    产品经理国际资格认证NPDP是新产品开发方面的认证,集理论、方法与实践为一体的全方位的知识体系,为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会(PDMA)成立于1979年,是全球范围内产品开发与管理专业人士最杰出的倡导者,协助个人、企业或......
  • USB的DP(D-) DM(D+)的英文全称是什么?
    DM:dataminus负,DP:dataplus正。 VCC是电源5V(红色线),DM是USB的数据线D-(白色线),DP是USB的数据线D-+(绿色线),GND是地(黑色线),; USB插头线一般的排列方式是VCC、D-、D+、GND。 Digital Positive&DigitalMinus。USB的通信都是由主机发起的,这一点与IIC协议是类似的。US......