动态规划之树形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; // 不参加party
    dp[u][1] = value[u]; // 参加party
    for(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;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp实现地图点聚合

点聚合的最重要的一个地方是在 markers 中添加 joinCluster true 这个重要的属性&#xff0c;否则将无法开启点聚合功能。 其实在uniapp的官方文档里体现的不是那么清楚&#xff0c;但是在小程序文档提示的就相当清楚。 实现效果如下&#xff1a; 重点&#xff1a;需要编译在小…

git下载太慢

git官网下载git太慢 阿里git地址 下载适合自己的版本

HTTP请求走私漏洞简单分析

文章目录 HTTP请求走私漏洞的产生HTTP请求走私漏洞的分类HTTP请求走私攻击的危害确认HTTP请求走私漏洞通过时间延迟技术确认CL漏洞通过时间延迟技术寻找TE.CL漏洞 使用差异响应内容确认漏洞通过差异响应确认CL.TE漏洞通过差异响应确认TE.CL漏洞 请求走私漏洞的利用通过请求漏洞…

ARTS 挑战打卡的第1天 --- Linux驱动与设备的匹配规则(Tips)

前言 &#xff08;1&#xff09;因为在Linux驱动开发中&#xff0c;驱动可以和设备c文件文件进行匹配&#xff0c;也可以和设备树dts文件进行匹配。为了弄明白驱动与他们的匹配规则&#xff0c;我查阅了一些资料同时阅读了源码&#xff0c;最终打算使用图片的方式形象具体的写成…

FFmpeg5.0源码阅读——av_interleaved_write_frame

摘要&#xff1a;本文主要详细描述FFmpeg中封装时写packet到媒体文件的函数av_interleaved_write_frame的实现。   关键字&#xff1a;av_interleaved_write_frame   读者须知&#xff1a;读者需要熟悉ffmpeg的基本使用。 1 基本调用流程 av_interleaved_write_frame的基本…

matlab使用教程(6)—线性方程组的求解

进行科学计算时&#xff0c;最重要的一个问题是对联立线性方程组求解。在矩阵表示法中&#xff0c;常见问题采用以下形式&#xff1a;给定两个矩阵 A 和 b&#xff0c;是否存在一个唯一矩阵 x 使 Ax b 或 xA b&#xff1f; 考虑一维示例具有指导意义。例如&#xff0c;方程 …

Redis - 缓存的双写一致性

概念&#xff1a; 当修改了数据库的数据也要同时更新缓存的数据&#xff0c;缓存和数据库的数据要保持一致 那为什么会有不一致的情况呢&#xff1f; 如果不追求一致性&#xff0c;正常有两种做法 先修改数据库 后删除旧的缓存先删除旧的缓存 再修改数据库 我们以先删除旧的…

【玩转pandas系列】数据清洗(文末送书福利)

文章目录 一、重复值检测二、元素替换1️⃣ 元素替换replace2️⃣ 数据映射map 三、修改索引1️⃣ 修改索引名rename2️⃣ 设置索引和重置索引 四、数据处理1️⃣ apply与applymap2️⃣ transform 五、异常值处理六、抽样聚合函数1️⃣ 抽样2️⃣ 数学函数 七、分组聚合&#x…

数字世界未来十年面貌如何?

随着科技的不断发展和创新&#xff0c;数字世界将在未来十年迎来一系列革命性的变化和进步。以下是数字世界未来十年面貌的一些预测&#xff1a; 人工智能全面普及&#xff1a;人工智能将逐渐渗透到我们生活的方方面面。从智能家居到智能交通&#xff0c;从个性化医疗到智能零售…

用python编写一个小程序,如何用python编写软件

大家好&#xff0c;给大家分享一下用python编写一个小程序&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 1、python可以写手机应用程序吗&#xff1f; 我想有人曲解意思了&#xff0c;人家说用python开发渣蔽一个手机app&#xff0c;不是…

零基础C#编写上位机如何入门?

学习C#基础语法和.NET框架&#xff0c;掌握基本编程概念和语法&#xff0c;例如数据类型、类、对象、继承、多态、异常处理等。学习WinForm窗体应用程序开发技术&#xff0c;掌握窗体应用程序的设计和开发&#xff0c;例如控件的使用、事件驱动编程、窗体的布局与设计等。学习数…

《向量数据库指南》——Milvus Cloud 2.3 和 2.4 版本的重要变化

Milvus Cloud2.3 和 2.4 版本的重要变化。 首先是 Milvus Cloud2.3 将支持 Json 数据类型,在此基础上亦会支持 Schemaless。此前,用户在使用 Milvus Cloud的过程中会先定一个静态 Schema,此时,如果在实际业务层面如果多了几个 feature 或者 Metadata,就意味着数据需要重新…

什么是架构 架构图

如何成为一名架构师,架构师成长之路_golang架构师成长之路_个人渣记录仅为自己搜索用的博客-CSDN博客 如何画架构图_个人渣记录仅为自己搜索用的博客-CSDN博客 如何画好一张架构图&#xff1f;&#xff08;内含知识图谱&#xff09; - 知乎 什么是架构&#xff1f;要表达的到…

代码随想录算法训练营第二十八天 | Leetcode随机抽题检测

Leetcode随机抽题检测--使用题库&#xff1a;Leetcode热题100 1 两数之和未看解答自己编写的青春版重点题解的代码日后再次复习重新写 49 字母异位词分组未看解答自己编写的青春版重点题解的代码日后再次复习重新写 128 最长连续序列未看解答自己编写的青春版重点关于 left 和 …

黑客技术(网络安全)学习笔记

一、网络安全基础知识 1.计算机基础知识 了解了计算机的硬件、软件、操作系统和网络结构等基础知识&#xff0c;可以帮助您更好地理解网络安全的概念和技术。 2.网络基础知识 了解了网络的结构、协议、服务和安全问题&#xff0c;可以帮助您更好地解决网络安全的原理和技术…

Qt 2. QSerialPortInfo显示串口信息

在ex2.pro 添加&#xff1a; QT serialport//main.cpp #include "ex2.h" #include <QtSerialPort/QtSerialPort> #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Ex2 w;w.show();QList<QSerialPortInfo>…

出海拍|疯了!ChatGPT 大规模封号,有钱还不赚?

时代洪流裹挟命运流转&#xff0c;世事变迁暗藏跨境沉浮。 几个月前&#xff0c;OpenAI的创始人之一马斯克曾在社交媒体平台上称赞ChatGPT &#xff1a;“Its a new world. Goodbye homework!”而他所说的ChatGPT 引领着许多AI工具&#xff0c;随后便开始如雨后春笋般出现在我…

刷题学算法

刷题学算法 数据结构 一、数组 1. 数组创建&#xff1a; // 方式1&#xff1a;先创建&#xff0c;再逐个存储元素 String[] cityArray1 new String[5]; cityArray1[0] "北京"; cityArray1[1] "上海"; cityArray1[2] "广州"; cityArray1[3…

【RabbitMQ(day4)】SpringBoot整合RabbitMQ与MQ应用场景说明

一、SpringBoot 中使用 RabbitMQ 导入对应的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>配置配置文件 spring:application:name: rabbitmq-springbo…

linux备份与还原系统(类似window上ghost备份还原)

一、摘要 在linux上进行了几年的开发工作 &#xff08;qt ros&#xff09; 突然发现&#xff0c;现在有公司硬件、笔记本台式机一台占一个系统&#xff0c;导致硬件太浪费&#xff0c;又不能用虚拟机&#xff08;有时候要链接硬件必须物理机&#xff09;怎么办&#xff1f; 二…
最新文章