算法:树形dp(树状dp)

news2024/11/24 21:08:39

文章目录

    • 一、树形DP的概念
      • 1.基本概念
      • 2.解题步骤
      • 3.树形DP数据结构
    • 二、典型例题
      • 1.LeetCode:337. 打家劫舍 III
        • 1.1、定义状态转移方程
        • 1.2、参考代码
      • 2.ACWing:285. 没有上司的舞会
        • 1.1、定义状态转移方程
        • 1.2、拓扑排序参考代码
        • 1.3、dfs后序遍历参考代码

一、树形DP的概念

  树形动态规划(Tree DP) 是动态规划在树结构数据上的应用。树是一种特殊的图,具有无环的特点,这使得树形动态规划在很多问题中比普通的动态规划更为直观和高效。在树形DP问题中,通常需要处理与节点相关的状态,并利用树的层次结构,自底向上或自顶向下递归地解决问题。树形DP的学习对线性DP的状态定义也有很大帮助。

1.基本概念

  • 状态表示:树形DP的状态通常与树的节点相关联,每个状态代表了以某节点为根的子树在某种条件下的最优解、计数或其他性质。
  • 状态转移:状态的转移依赖于子节点到父节点(或父节点到子节点)的关系。通常,要解决一个节点的问题,需要先解决它的所有子节点的问题,然后根据子节点的解来更新当前节点的解。或者当前结点的状态由父亲转移而来。

  在树形DP中,如果当前结点的状态由其子节点转移而来,那么可以进行DFS后序遍历,或者拓扑排序顺序遍历(在图中)。比如在关键路径中,提到过,求最长路径可以使用拓扑排序+动态规划来求解,这实际上就很像是一个树形DP。

2.解题步骤

  我们说树形DP的状态转移,实际上比线性DP更直观,因为父子结点之间的关系是很明显的,是从上到下的状态转移还是从下依次往下的状态转移是很明显的。

  1. 定义状态:首先明确每个状态所代表的含义,以及解题需要考虑的所有情况。
  2. 状态转移方程:根据问题的具体条件,找出不同状态之间的关系,形成状态转移方程。这通常涉及对当前节点的处理和对子节点状态的汇总。
  3. 初始化:确定递归的基本情况,即最简单情况下各状态的值,用于终止递归。
  4. 计算顺序:确定状态计算的顺序。在树形DP中,通常需要后序遍历(先处理所有子节点,再处理父节点)来自底向上计算,或者先序遍历来自顶向下传递信息。
  5. 答案提取:根据定义的状态,从根节点或特定节点提取最终答案。

3.树形DP数据结构

线性dp直接可以使用线性数组来存储状态,那么树形DP怎么办呢?

  • 如果真的只跟其亲儿子有关,你可以直接在树上进行DFS后序,利用DFS函数中存储状态,因为这不涉及到记忆化搜索
  • 如果其不仅仅跟其亲儿子有关,你就必须采取一定行动来保存状态信息了(属于一种记忆化搜索)。
  • 你可以先进行一次DFS对树结点编号(或已经有编号),然后再操作。这样你就可以定义dp数组了
  • 你可以对树结点存储在哈希表中来存储该结点状态。这样你就可以定义dp了
  • 如果需要新建图,你可以对树或图,存储其邻接表,并且每个结点多定义一个状态信息即可。若对于一个特定的顶点状态,它跟其邻接表中所有结点有关,如果其邻接表中的顶点状态已经被求出,则可以进行转移。树图不分家。vector实现邻接表
  • ···

二、典型例题

1.LeetCode:337. 打家劫舍 III

模板题
337. 打家劫舍 III
在这里插入图片描述
  父亲最优解跟其子节点最优解有关,因此属于树状dp。由于父亲的最优解不仅跟其亲儿子有关,还跟孙子有关,因此我们在进行树状dp时,不能只DFS后序遍历返回结果,我们还得存储孙子信息,即记忆化搜索,因此我们在书写这个树状dp的时候,需要使用额外空间存储状态,比如在此题中没有给出编号,使用哈希表是最快的。
  但是以上我们所说的状态是单个状态 即 dp[root]表示以root为根的最高金额,如果我们定义的状态dp[root][0]表示不选择root时的最高金额,dp[root][1]表示选择root时的最高金额,那么我们只需要将树返回值是多个值(如结构体),就可以不用多开额外空间了~

1.1、定义状态转移方程
  • dp[root][0]=max(dp[l_child][1],dp[l_child][0])+max(dp[r_child][1],dp[r_child][0]);//不被选择时
  • dp[root][1]=root->val+dp[l_child][0]+dp[r_child][0];//被选择时
    • 根不被选择时,其能得到的最大金额的状态,由其儿子不被选择的最大金额状态以及其儿子被选择的最大金额状态转移而来,因为根不被选择时,其儿子可以被选也可以不被选,让根的金额最大,那么择其最大者就行。
    • 根被选择时,其能得到的最大金额的状态,由其儿子不被选择的最大金额状态转移而来。
1.2、参考代码
class Solution {
public:
	//pair::first表示不被选择时的最大值
	//pair::second表示被选择时的最大值
    pair<int,int> TreeDp(TreeNode * root){
        if(!root) return {0,0};
        pair<int,int> dp_left=TreeDp(root->left);
        pair<int,int> dp_right=TreeDp(root->right);
        pair<int,int> dp_root;
        //不被选择时
        dp_root.first=max(dp_left.first,dp_left.second)+max(dp_right.first,dp_right.second);
        //被选择时
        dp_root.second=root->val+dp_left.first+dp_right.first;
        return dp_root;
    }
    int rob(TreeNode* root) {
        pair<int,int> ans=TreeDp(root);
        return max(ans.first,ans.second);
    }
};

2.ACWing:285. 没有上司的舞会

模板题
285. 没有上司的舞会
在这里插入图片描述
  这题和 打家劫舍 III 基本上一摸一样呀,只不过这里可能是图,并且acwing需要自己构建结点,因此这里可以提供更多建树dp思路。

没有职员愿意和直接上司一起参会。实际上就是指相邻父子结点不能同时被选择。

1.1、定义状态转移方程

  这里我们的状态和打家劫舍III的状态一模一样。不过我们可以使用拓扑排序的方式实现,只需要存储fa数组,表示父节点,因为根结点的状态由子节点转移而来,并且没必要按顺序转移~。用邻接表存图,然后按照打家劫舍III dfs实现会简单很多。

1.2、拓扑排序参考代码
#include<bits/stdc++.h>
using namespace  std;
int main(void){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int N;
    cin>>N;
    vector<int> fa(N+1,-1);//拓扑排序方法 找寻前驱用
    vector<int> degree(N+1,0);//用于拓扑排序删除度的数组
    vector<pair<int,int>> dp(N+1);//dp状态数组,second表示被选择,first表示不被选择。
    for(int i=1;i<=N;++i) {cin>>dp[i].second;dp[i].first=0;}
    for(int i=1;i<N;++i){
        int son,Fa;
        cin>>son>>Fa;
        fa[son]=Fa;
        degree[Fa]++;
    }
    //为了和打家劫舍不一样,我们这里采用拓扑排序的方式
    //如果仍然用dfs,则需要找到根结点,dfs会方便很多,也不需要fa数组了
    stack<int> sta;
    for(int i=1;i<=N;++i){
        if(degree[i]==0){
            sta.push(i);
        }
    }
    int ans=0;
    while(!sta.empty()){
        int cur=sta.top();sta.pop();
        if(fa[cur]!=-1) {
            dp[fa[cur]].first += max(dp[cur].first, dp[cur].second);
            dp[fa[cur]].second += dp[cur].first;
            if (--degree[fa[cur]] == 0) sta.push(fa[cur]);
        }else
            ans=max(ans,max(dp[cur].first,dp[cur].second));
    }
    cout<<ans;
    return 0;
}
1.3、dfs后序遍历参考代码
#include<bits/stdc++.h>
using namespace  std;
vector<vector<int> > g;
vector<pair<int,int>> dp;//dp状态数组,second表示被选择,first表示不被选择。
void dfs(int root){
	for(int i=0;i<g[root].size();++i){
		dfs(g[root][i]);
		dp[root].second+=dp[g[root][i]].first;
		dp[root].first+=max(dp[g[root][i]].first,dp[g[root][i]].second);
	}
	return;
}
int main(void){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int N;
    cin>>N;
    g.assign(N+1,vector<int>{});
    dp.assign(N+1,{});//dp状态数组,second表示被选择,first表示不被选择。
    unordered_set<int> roots;//用于保存根
    for(int i=1;i<=N;++i) {cin>>dp[i].second;dp[i].first=0;roots.insert(i);}
    for(int i=1;i<N;++i){
        int son,Fa;
        cin>>son>>Fa;
        g[Fa].emplace_back(son);
        roots.erase(son);
    }
    int ans=0;
    for(auto & i:roots){
		dfs(i);ans+=max(dp[i].first,dp[i].second);//从不同根遍历
	}
    cout<<ans;
    return 0;
}

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

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

相关文章

【可视化大屏开发】18. 加餐-ECharts+百度地图API实现热力图

ECharts结合百度地图API能获得更好的使用体验。 效果展示 放大后的效果 切换卫星地图模式 实现步骤 1. 通过Python实现GPS数据模拟 2. 通过IDEA开发地图 通过Python实现GPS数据模拟 import random from math import cos, sin, radians, sqrt import jsondef generate_random…

【web网页制作】html+css网页制作学校网站主题校园网页(5页面)【附源码】

学校网页制作目录 涉及知识写在前面一、网页主题二、网页效果Page1、简介Page2、校园风光Page3、学术研究Page4、校训阐述Page5、留言 三、网页架构与技术3.1 脑海构思3.2 整体布局3.3 技术说明书 四、网页源码4.1 主页模块源码4.2 源码获取方式 作者寄语 涉及知识 学校网站主…

【云计算】云网络产品体系概述

云网络产品体系概述 在介绍云网络产品体系前&#xff0c;先介绍几个与云计算相关的基础概念。 阿里云在基础设施层面分为 地域 和 可用区 两层&#xff0c;关系如下图所示。在一个地域内有多个可用区&#xff0c;每个地域完全独立&#xff0c;每个可用区完全隔离&#xff0c;同…

低噪声、低电压、低功耗特点的双运算放大器D722

概述 Silicore D722具有低噪声、低电压、低功耗特点的双运算放大器&#xff0c;具有9MHz的高增益带宽积&#xff0c;转换率为8.5V/μs, 5V时静态电流为0.97mA&#xff0f;每个放大器。适用于低电压和低噪声场合的应用系统&#xff0c;它提供轨到轨的输出摆幅&#xff0c;符合工…

【C++】1.从C语言转向C++

目录 一.对C的认识 二.C的关键字 三.命名空间 3.1命名空间的定义 3.2命名空间的使用 四.C的输入与输出 五.缺省参数 5.1全缺省参数 5.2半缺省参数 六.函数重载 七.引用 7.1引用的特性 7.2引用和指针的区别 八.内联函数 九.auto关键字&#xff08;C1…

面试:如何设计一个注册中心?

大家好&#xff0c;我是田哥 上周&#xff0c;一位群里的朋友反馈面试情况&#xff1a; 今天&#xff0c;给大家分享如何设计一个注册中心。其实这个问题&#xff0c;我之前在知识星球里分享过&#xff0c;可能是因为时间比较久了&#xff0c;加上这位朋友加入不久&#xff0c;…

提升编程效率的秘密武器:IntelliJ IDEA

IntelliJ IDEA的基本介绍 正如一个故事的开头&#xff0c;我们从一个名字开始 - IntelliJ IDEA。这是一个在程序员中广受欢迎的集成开发环境&#xff08;IDE&#xff09;&#xff0c;由捷克公司JetBrains开发。它的名字听起来有些复杂&#xff0c;但实际上&#xff0c;它的功能…

Windows联网状态工具TCPView

文章目录 TCPView命令行工具更多Sysinternals Suite工具 TCPView TCPView用于显示系统上所有 TCP 和 UDP 终结点的详细列表&#xff0c;包括本地和远程地址以及 TCP 连接的状态&#xff0c;界面如下。 列表的表头含义如下 表头含义表头含义Process name应用名称Process id进程…

Web App 入门指南:构建预测模型 App 的利器(shiny)

Web App 入门指南&#xff1a;构建预测模型 App 的利器 简介 近年来&#xff0c;随着机器学习和人工智能技术的快速发展&#xff0c;预测模型在各行各业得到了广泛应用。为了方便地部署和使用预测模型&#xff0c;将模型构建成 Web App 是一种非常好的选择。Web App 无需下载…

【vue】v-if 条件渲染

v-if 不适用于频繁切换显示模式的场景 修改web.user&#xff0c;可看到条件渲染的效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initi…

ChatGPT 会被 OpenAI 的版权诉讼摧毁吗?|TodayAI

未来人工智能是否能与人类融合成为一个引人关注的问题&#xff0c;但目前&#xff0c;ChatGPT等人工智能技术可能首先需要面对一个更紧迫的挑战&#xff1a;大规模的版权侵权诉讼。近期&#xff0c;ChatGPT因涉嫌在未经授权的情况下使用大量作者的作品进行训练&#xff0c;而被…

元象4.2B参数 MoE大模型实战

01 简介 近期&#xff0c;元象公司推出了其首个Moe大模型XVERSE-MoE-A4.2B。该模型采用了混合专家模型架构&#xff08;Mixture of Experts&#xff09;&#xff0c;并拥有4.2B的激活参数&#xff0c;其性能可与13B模型相媲美。值得一提的是&#xff0c;这个模型是完全开源的&…

算法设计与分析实验报告c++实现(最近点对问题、循环赛日程安排问题、排序问题、棋盘覆盖问题)

一、实验目的 1&#xff0e;加深学生对分治法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、最…

《剑指 Offer》专项突破版 - 面试题 107 : 矩阵中的距离(C++ 实现)

题目链接&#xff1a;矩阵中的距离 题目&#xff1a; 输入一个由 0、1 组成的矩阵 M&#xff0c;请输出一个大小相同的矩阵 D&#xff0c;矩阵 D 中的每个格子是矩阵 M 中对应格子离最近的 0 的距离。水平或竖直方向相邻的两个格子的距离为 1。假设矩阵 M 中至少有一个 0。 …

Google 推出 Gemini 1.5 Pro能处理音频;iOS 18或带来Safari 浏览助手;Llama 3 开源模型下个月推出

Google 推出 Gemini 1.5 Pro 公共预览版&#xff0c;能处理音频 Google 宣布将通过其 AI 应用平台 Vertex AI 向公众提供 Gemini 1.5 Pro&#xff0c;并且还赋予其「听力」&#xff0c;帮助用户处理音频内容。 用户可以上传会议录音、电视节目等音频内容&#xff0c;无需书面记…

Python计算多个表格中多列数据的平均值与标准差并导出为新的Excel文件

本文介绍基于Python语言&#xff0c;对一个或多个表格文件中多列数据分别计算平均值与标准差&#xff0c;随后将多列数据对应的这2个数据结果导出为新的表格文件的方法。 首先&#xff0c;来看一下本文的需求。现有2个.csv格式的表格文件&#xff0c;其每1列表示1个变量&#x…

如何入门做物联网系统压测?

文章目录 一、政策解读二、MQTT 压测常见场景三、MQTT常见业务场景1、并发连接2、消息吞吐量测试2.1 1 对 12.2 多对1&#xff08;上报&#xff09;2.3 1对多2.4 其它场景 三、MQTT常见性能指标四、MQTT常见性能工具1、emqtt-bench2、JMeter 五、小结 一、政策解读 微信公众号…

功能测试_验证标题长度合法性_边界值法

验证标题长度合法性&#xff08;标题长度大于0&#xff0c;小于等于30个字符&#xff09; 开内闭外&#xff0c;保留1和31

《web应用技术》第三次课后练习

实验目的&#xff1a; 1、springboot入门程序撰写并启动 2、使用postman练习参数的获取。 参考&#xff1a;Day04-10. Web入门-SpringBootWeb-快速入门_哔哩哔哩_bilibili

Java | Leetcode Java题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; class Solution {public int threeSumClosest(int[] nums, int target) {Arrays.sort(nums);int n nums.length;int best 10000000;// 枚举 afor (int i 0; i < n; i) {// 保证和上一次枚举的元素不相等if (i > 0 && nums…