当前位置: 首页 > news >正文

动态规划之树形DP

动态规划之树形DP

  • 树形DP
    • 何为树形DP
  • 树形DP例题
    • HDU-1520 Anniversary party
    • HDU-2196 Computer
    • 834. 树中距离之和

树形DP

何为树形DP

树形DP是指在“树”这种数据结构上进行的动态规划:给出一颗树,要求以最少的代价(或取得最大收益)完成给定的操作。通常这类问题规模比较大,枚举算法效率低,无法胜任,贪心算法不能求得最优解,因此需要用动态规划进行求解。

在树上做动态规划显得非常合适,因为树本身有“子结构”性质(树和子树),具有递归性,符合DP性质。相比线性DP,树形DP的状态转移方程更加直观。

树形动态规划(Tree DP)是一种动态规划算法,在处理树状结构(例如树、森林、有向无环图等)上的问题时非常常见和有效。树形动态规划通过将问题拆解为子问题,并利用子问题的解来求解更大规模的问题。

在树形动态规划中,我们需要定义一个适合的状态和状态转移方程。一般来说,状态可以定义为以当前节点为根的子树的某种性质,例如最大路径和、最长路径长度、最大权值和等等。而状态转移方程则描述了如何由子节点的状态计算当前节点的状态。

树形动态规划的典型做法是使用深度优先搜索(DFS)遍历整个树,在遍历过程中进行状态的计算和更新。通过递归地计算子节点的状态,并将其传递给父节点,可以得到整个树的最终状态。

在实现树形动态规划时,需要注意避免重复计算,可以使用记忆化搜索或者自底向上的方式进行计算。此外,还要注意选择合适的遍历顺序,以保证子问题的状态在计算当前节点状态时已经求解完毕。

总而言之,树形动态规划是一种针对树状结构问题的动态规划算法,通过拆解问题为子问题,并利用子问题的解求解更大规模的问题。它在解决树相关的问题时具有重要的应用价值。

树形DP例题

HDU-1520 Anniversary party

HDU-1520 Anniversary party

image-20230724114827116

题目大意

邀请员工参加party,但是为了避免员工和直属上司发生尴尬,规定员工和直属上司不能同时出席。

也就是每个人代表树中一个结点,每个结点拥有一个权值,相邻的父结点和子结点只能选择一个,问如何取才能使总权值之和最大。

员工编号从1到N。第一行输入包含数字N。1 < = N < = 6000。随后的N行中的每一行都包含相应员工的愉快度评级。欢乐评级是一个介于-128到127之间的整数。然后是描述主管关系树的T行。树规范的每一行都具有以下形式: L K 这意味着第K个员工是第L个员工的直接主管。输入以一行结束 0 0

解题思路

根据DP的解题思路,定义状态为:

d p [ i ] [ 0 ] dp[i][0] dp[i][0],表示不选择当前结点时候的最优解

d p [ i ] [ 1 ] dp[i][1] dp[i][1],表示选择当前结点时候的最优解

其中状态转移方程分为下面两种情况:

  1. 不选择当前结点,则子结点可选可不选,取其中的最大值即可,也就是 d p [ u ] [ 0 ] + = m a x ( d p [ s o n ] [ 0 ] , d p [ s o n ] [ 1 ] ) dp[u][0] += max(dp[son][0], dp[son][1]) dp[u][0]+=max(dp[son][0],dp[son][1])
  2. 选择当前结点,则其子结点不能选, d p [ u ] [ 1 ] + = d p [ s o n ] [ 0 ] dp[u][1] += dp[son][0] dp[u][1]+=dp[son][0]

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 6010;
vector<int>tree[maxn];
int dp[maxn][2], father[maxn], value[maxn];
void dfs(int u) {dp[u][0] = 0; // 不参加partydp[u][1] = value[u]; // 参加partyfor(int i = 0; i < tree[u].size(); i++) {int son = tree[u][i];dfs(son); // 深搜子结点dp[u][0] += max(dp[son][0], dp[son][1]); // 父结点不选,子结点可选可不选dp[u][1] += dp[son][0]; // 父结点选择,子结点不能选}
}
int main() {int n, a, b;while(~scanf("%d", &n)) {for(int i = 1; i <= n; i++) {scanf("%d", &value[i]);tree[i].clear();father[i] = -1;}memset(dp, 0, sizeof(dp));while(true) { // 建树scanf("%d%d", &a, &b);if(a == 0 && b == 0) break;tree[b].push_back(a);father[a] = b; // 父子关系,表示a的父亲是b}for(int i = 1; i <= n; i++) {if(father[i] == -1) { // 寻找父结点,从父结点开始深搜dfs(i);printf("%d\n", max(dp[i][0], dp[i][1]));break;}}}return 0;
}

HDU-2196 Computer

HDU-2196 Computer

image-20230729105959490

题目大意

一颗树,根节点的编号是1,对其中的任意一个结点,求离这个结点最远结点的距离。

输入包含多个测试用例,每个用例的第一行是一个自然数N,N不超过10000,接下来N-1行,每行输入两个整数:第一个整数为某结点id,第二个整数为连接到这个结点需要的距离。

输出N行,表示距离第i个结点的最远距离

解题思路

对于求解距离某结点的最长距离,应当分为两种情况:

对于任意一个结点

  1. 结点 i i i 的子树中的结点距离结点 i i i 的最长距离,而为了方便第二种情况的计算,第一次深搜的时候需要记录结点 i i i 的子树中某个结点到该结点的最长距离和第二长的距离。

  2. 第二种情况就是结点 i i i 往上走,而此时往上走又分为两种情况:

    从结点 i i i 往上继续走,没有走结点 i i i 的父结点到其子树的最长距离的路径

    若结点 i i i 沿着其父结点的最长路径上走时,则需考虑结点 i i i 是否在其父结点的最长子树上,此时则需要用到最初求得各个结点到其它结点的最长距离和次长距离,如果结点 i i i 在其父结点的最长子树上那么 X = two + dist(i, i 的父结点),否则 X = one + dist(i, i 的父结点)

综上所述,第一种情况和第二种情况求得两个值的最大值即为答案

状态的设计:结点 i i i 的子树到 i i i 的最长距离 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 以及次长距离 d p [ i ] [ 1 ] dp[i][1] dp[i][1],从结点 i i i 往上走的最长距离 d p [ i ] [ 2 ] dp[i][2] dp[i][2]

#include<bits/stdc++.h>
using namespace std;
struct Node {int id; // 子树结点编号int cost;
};
const int maxn = 10010;
int dp[maxn][3], n;
vector <Node> tree[maxn];
void init() { // 初始化和建树for(int i = 1; i <= n; i++) tree[i].clear();memset(dp, 0, sizeof(dp));int x, y;for(int i = 2; i <= n; i++) {scanf("%d%d", &x, &y);Node tmp;tmp.id = i;tmp.cost = y;tree[x].push_back(tmp);}
}
void dfs1(int father) { // 搜索以结点father为起点到子树的最长距离和次长距离int one = 0, two = 0; // one 记录father往下走的最长距离,two记录次长距离for(int i = 0; i < tree[father].size(); i++) { // 先处理子结点再处理父结点Node child = tree[father][i];dfs1(child.id); // 递归子结点,直到最底层int temp = child.cost + dp[child.id][0];if(temp >= one) {two = one;one = temp;}if(temp < one && temp > two) two = temp;}dp[father][0] = one; // 得到以father为起点的子树的最长距离dp[father][1] = two; // 得到以father为起点的子树的次长距离
}
void dfs2(int father) { // 先处理父结点再处理子结点for(int i = 0; i < tree[father].size(); i++) {Node child = tree[father][i];if(child.cost + dp[child.id][0] == dp[father][0]) // child在最长距离的子树上dp[child.id][2] = max(dp[father][1], dp[father][2]) + child.cost;else dp[child.id][2] = max(dp[father][0], dp[father][2]) + child.cost; //否则dfs2(child.id);}
}
int main() {while(~scanf("%d", &n)) {init();dfs1(1);dp[1][2] = 0;dfs2(1);for(int i = 1; i <= n; i++) {printf("%d\n", max(dp[i][0], dp[i][2]));}}return 0;
}

834. 树中距离之和

834. 树中距离之和

给定一个无向、连通的树。树中有 n 个标记为 0...n-1 的节点以及 n-1 条边 。

给定整数 n 和数组 edgesedges[i] = [ai, bi]表示树中的节点 aibi 之间有一条边。

返回长度为 n 的数组 answer ,其中 answer[i] 是树中第 i 个节点与所有其他节点之间的距离之和。

示例 1:

img

输入: n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
输出: [8,12,6,10,10,10]
解释: 树如图所示。
我们可以计算出 dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5) 
也就是 1 + 1 + 2 + 2 + 2 = 8。 因此,answer[0] = 8,以此类推。

参考灵神的题解写出来的,不得不说灵神 yyds,比官方给出的题解简单多了。

灵神的题解地址:传送门

灵神给出的解题思路如下图所示:

在这里插入图片描述

AC代码:

class Solution {
public:static const int maxn = 3e4 + 10;vector<int>tree[maxn];vector<int> sumOfDistancesInTree(int n, vector<vector<int>>& edges) {for(auto edge : edges) {tree[edge[0]].push_back(edge[1]);tree[edge[1]].push_back(edge[0]);}vector<int>ans(n);vector<int>children(n, 1); // 每颗子树所包含结点数量function<void(int, int, int)> dfs1 = [&](int child, int father, int depth) {ans[0] += depth;for(auto i : tree[child]) {if(i != father) { // 避免访问父结点dfs1(i, child, depth + 1);children[child] += children[i]; // 累加子树大小}}};dfs1(0, -1, 0); // 0 没有父结点function<void(int, int)> dfs = [&](int child, int father) {for(auto i : tree[child]) {if(i != father) {ans[i] += (ans[child] + n - children[i] * 2);dfs(i, child);}}};dfs(0, -1);return ans;}
};

相关文章:

动态规划之树形DP

动态规划之树形DP 树形DP何为树形DP 树形DP例题HDU-1520 Anniversary partyHDU-2196 Computer834. 树中距离之和 树形DP 何为树形DP 树形DP是指在“树”这种数据结构上进行的动态规划&#xff1a;给出一颗树&#xff0c;要求以最少的代价&#xff08;或取得最大收益&#xff…...

嵌入式_GD32使用宏开关进行Debug串口打印调试

嵌入式_GD32使用宏开关进行Debug串口打印调试 串口Debug是一种将数据通过串口发送的方法。通过使用printf函数&#xff0c;我们可以将需要发送的数据格式化为字符串&#xff0c;并通过串口发送出去。在C语言中&#xff0c;通常使用串口发送数据的函数为printf函数&#xff0c;…...

使用 GitHub Copilot 进行 Prompt Engineering 的初学者指南(译)

文章目录 什么是 GitHub Copilot ?GitHub Copilot 可以自己编码吗&#xff1f;GitHub Copilot 的底层是如何工作的&#xff1f;什么是 prompt engineering?这是 prompt engineering 的另一个例子 使用 GitHub Copilot 进行 prompt engineering 的最佳实践提供高级上下文&…...

c++开发模式,享元模式

享元模式&#xff0c;个人理解&#xff0c;就是应用共享技术来减少类的对象创建&#xff0c;节省计算机资源消耗&#xff0c;而且能够减少维护成本 #include <iostream> #include <string> #include <vector>using namespace std;class Flyweight { public:…...

LLM大模型——langchain相关知识总结

目录 一、简介LangChain的主要价值支柱简单安装 二、 LangChain的主要模块1.Model I/Oprompt模版定义调用语言模型 2. 数据连接3. chains4. Agents5. MemoryCallbacks 三、其他记录多进程调用 主要参考以下开源文档 文档地址&#xff1a;https://python.langchain.com/en/lates…...

【Python】数据可视化利器PyCharts在测试工作中的应用

目录 PyCharts 简介 PyCharts 的安装 缺陷统计 测试用例执行情况 使用JavaScript情况 缺陷趋势分析 将两张图放在一个组合里&#xff08;grid&#xff09; 将两张图重叠成一张图&#xff08;overlap&#xff09; 将多张图组合在一个page 中&#xff08;page&#xff0…...

AOP的实战(统一功能处理模块)

一、用户登录权限效验 用户登录权限的发展从之前每个方法中自己验证用户登录权限&#xff0c;到现在统一的用户登录验证处理&#xff0c;它是一个逐渐完善和逐渐优化的过程。 1.1 最初用户登录验证 我们先来回顾一下最初用户登录验证的实现方法&#xff1a; RestController…...

时间复杂度为O(n2)的三种简单排序算法

1.冒泡排序 冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较&#xff0c;看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少少一个元素移动到它应该在的位置&#xff0c;重复n次&#xff0c;就完成了n个数据的排序工作。 /*** …...

LeetCode 热题 100 JavaScript --226. 翻转二叉树

给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 3&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[] 提示&#xff1a; 树中节点数目范围在 [0, 100] 内 -100 < Node.val < 100 var invertTree function(root…...

hive所有窗口函数详情总结

hive窗口函数详情总结 解释语法hive开窗函数排序开窗函数样例数据RANK()DENSE_RANK()ROW_NUMBER() 分析开窗函数样例数据&#xff1a;last_valuefirst_valuelaglead 其他窗口函数cume_distpercent_rank 解释 开窗函数用于为行定义一个窗口&#xff08;指运算将要操作的行的集合…...

Talk | 新加坡国立大学博士生施宇钧:DragDiffusion-基于扩散模型的关键点拖拽图片编辑

本期为TechBeat人工智能社区第518期线上Talk&#xff01; 北京时间8月2日(周三)20:00&#xff0c; 新加坡国立大学博士生—施宇钧的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “DragDiffusion-基于扩散模型的关键点拖拽图片编辑”&#xff0c;他…...

22 | 贝叶斯分类算法

文章目录 介绍什么是贝叶斯分类算法?贝叶斯分类算法的应用场景贝叶斯定理贝叶斯定理的基本原理贝叶斯定理的公式推导贝叶斯定理的应用举例代码介绍 什么是贝叶斯分类算法? 贝叶斯分类算法是一类基于贝叶斯定理的分类技术。在统计分类任务中,这些算法使用特定的假设来建立特…...

java.sql.SQLSyntaxErrorException: ORA-00909: 参数个数无效

问题&#xff1a; 在Select里采用Contact(%,#name,%)报错参数个数无效 原因&#xff1a; 回想以前用Mysql的时候就是这样用的&#xff0c;没有问题&#xff0c;在这里就出问题了&#xff0c;所以确定问题在oracle数据库上&#xff0c;经过查询得知&#xff0c;oracle和mysql…...

数据结构8-哈希表

数据结构8-哈希表 动态分配内存方式&#xff1a; #include <stdio.h> #include <stdlib.h>#define SIZE 20struct DataItem {int data; int key; };struct DataItem* hashArray[SIZE]; struct DataItem* dummyItem; struct DataItem* item;//获取键值 int has…...

vue3引用Font-Awesome字体图标库

环境&#xff1a;vue3tsviteelement plus 介绍&#xff1a;这里安装引用的是Font-Awesome 6.x 版本&#xff0c;有专业版&#xff08;付费&#xff09;&#xff0c;这里只介绍免费版字体使用方法 一、安装 1.使用npm安装&#xff0c;终端打开项目目录或者命令行cd到目录文件夹…...

Python: Django 服务部署可能遇到的一些问题

502 bad gateway 不要用 python3 manage.py runserver 启动服务&#xff0c; 而要用&#xff1a; daphne -b 0.0.0.0 -p <端口> <工程名>.asgi:application此外&#xff0c;在 setting.py 中&#xff0c;修改&#xff1a; import osSECRET_KEY os.environ.get(D…...

Python爬虫时遇到连接超时解决方案

在进行Python爬虫任务时&#xff0c;经常会遇到连接超时&#xff08;TimeoutError&#xff09;错误。连接超时意味着爬虫无法在规定的时间内建立与目标服务器的连接&#xff0c;导致请求失败。为了帮助您解决这个常见的问题&#xff0c;本文将提供一些解决办法&#xff0c;并提…...

这所国字头双一流,根本招不满,学硕都没人报!

一、学校及专业介绍 中国民航大学&#xff0c;位于天津市&#xff0c;是民航局、天津市、教育部共建高校&#xff0c;是天津市“双一流”建设高校和高水平特色大学建设高校。 1.1 招生情况 2023年中国民航大学电子信息与自动化学院&#xff0c;初试考806信号与系统的一共有两…...

macos 查询端口占用 命令

在 macOS 上查询端口占用的命令是通过使用lsof&#xff08;list open files&#xff09;工具来实现的。 lsof可以显示当前系统中打开的文件&#xff08;包括网络连接和端口&#xff09;的相关信息。 打开终端应用程序&#xff08;Terminal&#xff09;&#xff0c;然后输入以下…...

无代码开发:打破传统开发模式,引领数字化转型新方向

随着数字化转型的加速&#xff0c;企业对于高效、便捷的软件开发需求愈发旺盛。无代码开发作为一种新兴的软件开发模式&#xff0c;以其可视化、模块化的开发方式&#xff0c;为数字化转型提供了新的方向。本文将从无代码开发的优势、应用场景、如何实现等方面进行详细解读&…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…...