分享 - 树形dp

news2024/11/17 19:39:55

树形 d p dp dp

例1 - 基础

链接:树上子链

练手

分析

  • 其实一看题就很显然的树形 d p dp dp
  • 子链在这里分为两种情况,如图黑链和红链在这里插入图片描述

思路

  • d p [ i ] dp[i] dp[i] 表示以 i i i 开头的红链的最大权值
  • 易得: d p [ i ] = m a x ( d p [ i ] , a [ u ] + d p [ v ] ) dp[i] = max(dp[i], a[u] + dp[v]) dp[i]=max(dp[i],a[u]+dp[v])
  • 黑链的更新你可以另设一个 d p dp dp ,也可以直接更新答案 a n s = m a x ( a n s , d p [ u ] + d p [ v ] ) ans = max(ans, dp[u] + dp[v]) ans=max(ans,dp[u]+dp[v])
  • 黑链存在的话一定是最长两条红链的和,应该说是两条子红链加父亲节点权值 你可以想想这样为什么行
#include <bits/stdc++.h>

using ll = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    
    std::vector<ll> a(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }
    
    std::vector<std::pair<int ,int>> e((n + 1) << 1);
    std::vector<int> head(n + 1);
    int cnt = 0;
    auto add = [&](int u, int v) {
        e[++cnt] = {v, head[u]}, head[u] = cnt;
    };
    for (int i = 1; i < n; ++i) {
        int u, v;
        std::cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    
    ll ans = -1e18;
    std::vector<ll> dp(n + 1);
    std::function<void(int, int)> dfs = [&](int u, int f) {
        dp[u] = a[u];
        for (int i = head[u]; i; i = e[i].second) {
            int v = e[i].first;
            if (v xor f) {
                dfs(v, u);
                ans = std::max(ans, dp[u] + dp[v]);
                dp[u] = std::max(dp[u], a[u] + dp[v]);
            }
        }
        ans = std::max(ans, dp[u]);
    };
    dfs(1, 0);
    std::cout << ans;
    
    return 0;
}

例2 - 进阶

链接:二叉苹果树

练手

分析

  • 树上背包

思路

  • d p [ i ] [ j ] dp[i][j] dp[i][j] 表示以 i i i 节点保留 j j j 条树枝最多苹果数量。
  • 易得: ∑ j = 2 m ∑ k = 1 , k < j s z [ v ] d p [ u ] [ j ] = m a x ( d p [ u ] [ j ] , d p [ u ] [ j − k ] + d p [ v ] [ k ] \sum\limits_{j=2}^m \sum\limits_{k=1,k<j}^{sz[v]}dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k] j=2mk=1,k<jsz[v]dp[u][j]=max(dp[u][j],dp[u][jk]+dp[v][k]
  • 注意:这里的二维其实相当于普通背包的一维,所以 j j j 的枚举应该倒序。
#include<bits/stdc++.h>

using ll = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, q;
    std::cin >> n >> q;
    ++q;

    std::vector<std::tuple<int, int, int>> e((n + 1) << 1);
    std::vector<int> head(n + 1);
    int cnt = 0;
    auto add = [&](int u, int v, int w) {
        e[++cnt] = {v, w, head[u]}, head[u] = cnt;
    };
    for (int i = 1; i < n; ++i) {
        int u, v, w;
        std::cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }

    std::vector<std::vector<int>> dp(n + 1, std::vector<int>(q + 1));
    std::vector<int> sz(n + 1);
    std::function<void(int, int, int)> dfs = [&](int u, int f, int m) {
        sz[u] = 1;
        for (int i = head[u]; i;) {
            auto [v, w, nxt] = e[i];
            if (f xor v) {
                dp[v][1] = w;
                dfs(v, u, m);
                sz[u] += sz[v];
                for (int j = m; j > 1; --j) {
                    for (int k = 1; k <= sz[v] and k < j; ++k) {
                        dp[u][j] = std::max(dp[u][j], dp[u][j - k] + dp[v][k]);
                    }
                }
            }
            i = nxt;
        }
    };
    dfs(1, 0, q);
    std::cout << dp[1][q];

    return 0;
}

例3 - 困难

链接:树上染色

练手,这个更难

分析

  • 可以考虑上题一样就背包思想,枚举。

思路

  • d p [ u ] [ j ] dp[u][j] dp[u][j] u u u 节点 染 j j j 个为黑色最大效益。
  • 易得: ∑ j = 0 m i n ( k , s z [ u ] ) ∑ l = 0 m i n ( s z [ v ] , l ) d p [ u ] [ j ] = m a x ( d p [ u ] [ j ] , d p [ u ] [ j − l ] + d p [ v ] [ l ] + v a l \sum\limits_{j=0}^{min(k,sz[u])} \sum\limits_{l=0}^{min(sz[v],l)}dp[u][j] = max(dp[u][j], dp[u][j-l]+dp[v][l]+val j=0min(k,sz[u])l=0min(sz[v],l)dp[u][j]=max(dp[u][j],dp[u][jl]+dp[v][l]+val
  • v a l val val 即是很常见的计算贡献的思想。
  • 考虑一条边把树分成两部分树 A A A ,树 B B B ,树 B B B 选择 l l l 个点染黑色,那么另外 k − l k - l kl 个黑色节点即在树 A A A,那么这条边就会被计算 l × ( k − l ) l \times (k-l) l×(kl) 次,贡献即为 l × ( k − l ) × w l\times(k-l)\times w l×(kl)×w 。树 B B B 的另外 s z [ B ] − l sz[B]-l sz[B]l 个节点即为白色,那么另外 n − k − ( s z [ B ] − l ) n-k-(sz[B]-l) nk(sz[B]l) 个白色节点即在树 A A A ,所以该边白色贡献为 ( s z [ B ] − l ) × ( n − k − ( s z [ B ] − l ) ) ∗ w (sz[B] - l) \times (n - k - (sz[B] - l)) * w (sz[B]l)×(nk(sz[B]l))w
  • 同样倒序枚举 j j j
#include <bits/stdc++.h>

using ll = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, k;
    std::cin >> n >> k;

    std::vector<std::tuple<int, ll, int>> e((n + 1) << 1);
    std::vector<int> head(n + 1);
    int cnt = 0;
    auto add = [&](int u, int v, ll w) {
        e[++cnt] = {v, w, head[u]}, head[u] = cnt;
    };
    for (int i = 1; i < n; ++i) {
        int u, v;
        ll w;
        std::cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }

    std::vector<std::vector<ll>> dp(n + 1, std::vector<ll>(k + 1, -1));
    std::vector<int> sz(n + 1);
    std::function<void(int, int)> dfs = [&](int u, int f) {
        sz[u] = 1;
        dp[u][0] = dp[u][1] = 0;
        for (int i = head[u]; i;) {
            auto [v, w, nxt] = e[i];
            if (v xor f) {
                dfs(v, u);
                sz[u] += sz[v];
                for (int j = std::min(k, sz[u]); j >= 0; --j) {
                    for (int l = 0; l <= std::min(sz[v], k); ++l) {
                        if (dp[u][j - l] not_eq -1 and j >= l) {
                            ll val = 1ll * l * (k - l) * w + 1ll * (sz[v] - l) * (n - k - (sz[v] - l)) * w;
                            dp[u][j] = std::max(dp[u][j], dp[u][j - l] + dp[v][l] + val);
                        }
                    }
                }
            }
            i = nxt;
        }
    };

    dfs(1, 0);
    std::cout << dp[1][k];

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1713519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

游泳时用什么耳机听歌好?精品榜前四游泳耳机揭秘,款款佳品!

游泳时用什么耳机听歌好&#xff1f;这无疑是众多水上运动爱好者的共同疑问。在享受游泳带来的清凉与畅快时&#xff0c;若能伴随着悦耳的音乐&#xff0c;无疑能让整个体验更加完美。然而&#xff0c;市面上的游泳耳机种类繁多&#xff0c;品质各异&#xff0c;如何选择一款既…

乐鑫ESP串口驱动安装,安装cp210x驱动

windows11安装cp210x驱动&#xff1a; 1&#xff1a;第一步官网下载驱动&#xff1a; 官网地址如下&#xff1a; CP210x USB to UART Bridge VCP Drivers - Silicon Labs 第二步&#xff1a;解压文件夹并安装如图所示&#xff1a; 3&#xff1a;第三步安装成功后会给你个提示…

一个小技巧轻松提升量化精度!IntactKV:保持关键词元无损的大语言模型量化方法

目录 摘要关键词元&#xff08;Pivot Tokens&#xff09;方法概述实验验证1. 权重量化2. KV Cache 量化3. 权重和激活值量化 参考文献 本文介绍我们针对大语言模型量化的工作 IntactKV&#xff0c;可以作为插件有效提升 GPTQ、AWQ、QuaRot 等现有主流量化方法效果。论文作者来自…

CPU对代码执行效率的优化,CPU的缓存、指令重排序

目录 一、CPU对代码执行效率的优化 1. 指令流水线&#xff08;Instruction Pipelining&#xff09; 2. 超标量架构&#xff08;Superscalar Architecture&#xff09; 3. 动态指令重排序&#xff08;Dynamic Instruction Reordering&#xff09; 4. 分支预测&#xff08;…

visio中画乘法器加法器符号

情景&#xff1a;在进行rtl设计时&#xff0c;需要画出简单的电路实现图&#xff0c;需要用到加法器&#xff0c;乘法器的符号。 位置&#xff1a;在visio的更多形状中&#xff0c;找到如下图的位置&#xff0c;进行添加&#xff0c;即可 最终效果&#xff1a; 可以满足我们日…

基于L1范数惩罚的稀疏正则化最小二乘心电信号降噪方法(Matlab R2021B)

L1范数正则化方法与Tikhonov正则化方法的最大差异在于采用L1范数正则化通常会得到一个稀疏向量&#xff0c;它的非零系数相对较少&#xff0c;而Tikhonov正则化方法的解通常具有所有的非零系数。即&#xff1a;L2范数正则化方法的解通常是非稀疏的&#xff0c;并且解的结果在一…

虹科Pico汽车示波器 | 免拆诊断案例 | 2010 款北京现代ix35车挡位偶尔锁定在4挡

故障现象 一辆2010款北京现代ix35车&#xff0c;搭载G4KD发动机和6速自动变速器&#xff0c;累计行驶里程约为23.6万km。该车行驶中急加速时&#xff0c;挡位偶尔锁定在4挡&#xff0c;同时发动机故障灯异常点亮。 故障诊断 该车自动变速器控制模块&#xff08;TCM&#xff0…

css-calc动态计算属性值无效

1.calc计算 可以使用css属性动态适应盒子的宽高&#xff0c;适用于布局中左侧固定宽或高&#xff0c;右侧宽度适应&#xff1b;右侧宽度等于calc(100vw - rightWidth); 2.属性值无效 3.解决 width: calc(100vw - 360px); 减号左右需要空格

Kivy 项目51斩百词 5

MRWord\pages\infopage\info.py def read_random_word(self) def read_random_word(self):"""随机读取一条数据"""sql "SELECT * FROM word WHERE id (SELECT word_id FROM today ORDER BY RANDOM() limit 1)"rows select_data(sq…

Google使用AI改进了 Sheets;开源视觉语言模型llama3v;开源情绪语音模型ChatTTS;

✨ 1: Google has improved Sheets with AI. Google 使用 AI 改进了 Sheets 您可以使用 Gemini 处理您的数据并将其变成老师。 优化您的数据 Gemini 了解您的数据并提出改进建议。 例如&#xff0c;它可以将重复数据转换为更实用的下拉框。 解释数据 通过单击双子座图标…

产品推荐 | 基于Xilinx Zynq-7015 FPGA的MYC-C7Z015开发板

一、产品概述 基于 Xilinx Zynq-7015&#xff0c;双Cortex-A9FPGA全可编程处理器&#xff1b;PS部分(ARM)与PL部分(FPGA)之间采用AXI高速片上总线通信&#xff0c;吉比特级带宽&#xff0c;突破传统ARMFPGA架构的通信瓶颈&#xff0c;通过PL部分(FPGA)灵活配置丰富的外设接口&…

电表自动抄表系统:智能时代的能源管理新方式

1.界定和功能 电表自动抄表系统是一种现代化电力计量技术&#xff0c;它利用先进的通讯技术和互联网&#xff0c;完成了远程控制、实时电磁能数据采集和处理。系统的主要作用包含全自动载入电表数据信息、实时检测电力应用情况、出现异常报案及其形成详尽能源使用报告&#xf…

OpenHarmony面向万物智联的应用框架的思考与探索

应用框架&#xff0c;是操作系统连接开发者生态&#xff0c;实现用户体验的关键基础设施。业务的飞速发展促进了应用框架不断演进和变化。 01►业界应用框架的演进 应用是用户使用操作系统/设备的入口&#xff0c;应用框架则是应用开发和运行的基础设施。以移动端为例&#x…

逍遥散人的“痛婚”,让《光夜》玩家悄悄破防了

网红博主的一场求婚&#xff0c;让《光与夜之恋》玩家破防了。 知名游戏博主逍遥散人发微博公布求婚成功&#xff0c;本来应该是一件喜事&#xff0c;但却因为求婚场景布满了《光与夜之恋》男主角之一陆沉的谷子&#xff08;周边&#xff09;&#xff0c;遭到了“6推”&#x…

什么是组态?什么是工业控制中的组态软件?

随着工业4.0和智能制造的发展&#xff0c;工控软件的应用越来越广泛&#xff0c;它们在提高生产效率、降低能耗和减少人力成本等方面发挥着越来越重要的作用。 什么是工控软件&#xff1f; 工控软件是指用于工业控制系统的软件&#xff0c;主要应用于各种生产过程控制、自动化…

6种经典的网页布局设计,你最喜欢哪个?

信息时代&#xff0c;我们每天都会浏览很多网页&#xff0c;但你有没有想过&#xff0c;让你停留在一个新网页的关键因素有哪些&#xff1f;毫无疑问&#xff0c;网页布局一定是关键因素之一。一个优秀的网页布局不仅可以让网站看起来更美观、更专业&#xff0c;还能够抓住用户…

如何顺利通过软考中级系统集成项目管理工程师?

中级资格的软考专业包括"信息系统"&#xff0c;属于软考的中级级别。熟悉软考的人都知道&#xff0c;软考分为初级、中级和高级三个级别&#xff0c;涵盖计算机软件、计算机网络、计算机应用技术、信息系统和信息服务五个专业&#xff0c;共设立了27个资格。本文将详…

代码随想录算法训练营第八天| 344.反转字符串、541. 反转字符串II、 卡码网:54.替换数字、151.翻转字符串里的单词、卡码网:55.右旋转字符串

344.反转字符串 题目链接&#xff1a; 344.反转字符串 文档讲解&#xff1a;代码随想录 状态&#xff1a;so easy 题解&#xff1a; public void reverseString(char[] s) {int left 0, right s.length - 1;char temp s[0];while (left < right) {temp s[left];s[left] …

linux下常用的终端命令

文章目录 1. MV移动文件、重命名文件1.1 移动文件&#xff1a;mv [选项] 源文件或目录 目标文件或目录1.2 文件重命名 2. 查找&#xff1a;文件&#xff0c;内容&#xff0c;统计文件2.1 find查找文件2.2 Linux查找文件内容 3. 查看当前用户4. linux修改文件所属用户和组5. 复制…

MiniPCIe/SATA双用插槽无法识别minipcie模块怎么回事!

在计算机和嵌入式系统设计中,MiniPCIe/SATA双用插槽作为一种高度集成的解决方案,提供了极大的灵活性与扩展能力。它不仅能够支持MiniPCIe接口的无线网卡、固态硬盘控制器等模块,还能适应SATA接口的硬盘或固态存储设备,大大丰富了系统配置的可能性。尽管设计初衷良好,但在实…