【图论C++】树的直径(DFS 与 DP动态规划)

》》》算法竞赛

/*** @file            * @author          jUicE_g2R(qq:3406291309)————彬(bin-必应)*						一个某双流一大学通信与信息专业大二在读	* * @brief           一直在竞赛算法学习的路上* * @copyright       2023.9* @COPYRIGHT			 原创技术笔记:转载需获得博主本人同意,且需标明转载源* @language        C++* @Version         1.0还在学习中  */
  • UpData Log👆 2023.9.27 更新进行中
  • Statement0🥇 一起进步
  • Statement1💯 有些描述是个人理解,可能不够标准,但能达其意

技术提升站点

文章目录

  • 》》》算法竞赛
  • 技术提升站点
    • 21-1 树的直径
      • 21-1-1 定义
      • 21-1-2 性质
      • 21-1-3 实现的方法 及 选择
        • 直通车——>树的存储方法:链式前向星
      • 21-1-4 法一:做两次DFS(或BFS)
        • DFS(BFS)为何不能用在有 负权值 的树里呢?
      • 21-1-5 法二:树形DP(动态规划)
        • 直通车——>DP算法求最大子序和
        • 为何 DP能解决 有 负权值 的树 的树直径问题?
        • 如何实现 动态规划?

21-1 树的直径

21-1-1 定义

树上 最远的两个节点之间 的距离被称为 树的直径,连接这两个点的路径 被称为 树的最长链

21-1-2 性质

  • 1 、这两个最远点一定是叶子节点 1、这 两个最远点 一定是 叶子节点 1、这两个最远点一定是叶子节点
  • 2 、距任意结点最远的点一定是直径的端点 2、距 任意结点最远的点 一定是 直径的端点 2、距任意结点最远的点一定是直径的端点
  • 3 、两棵树相连,新树的直径的两端点一定是原四个端点中的两个 3、两棵树相连,新树的直径的两端点一定是原四个端点中的两个 3、两棵树相连,新树的直径的两端点一定是原四个端点中的两个
  • 4 、若一棵树存在多条直径,多条直径交于一点,且交点是直径的严格中点(中点可能在某条边内) 4、若一棵树存在多条直径,多条直径交于一点,且交点是直径的严格中点(中点可能在某条边内) 4、若一棵树存在多条直径,多条直径交于一点,且交点是直径的严格中点(中点可能在某条边内)

21-1-3 实现的方法 及 选择

1)做两次DFS(或BFS)

2)树形DP

操作方法优点缺点
做两次DFS(或BFS)可以得到完整的路径,从而得到点与点之间的距离不能用于有 负权值 的树
树形DP能用于有 负权值 的树不可以得到完整的路径

树的直径

Input

就测试一个边上权值都为1的满二叉树

7
1 2 1
1 3 1
2 4 1
2 5 1
3 6 1
3 7 1

Output

4
直通车——>树的存储方法:链式前向星

21-1-4 法一:做两次DFS(或BFS)

  • 从任意 u 结点 u结点 u结点 出发,离 u 结点 u结点 u结点 最远的 e 结点 e结点 e结点,一定是该树直径的其中一个端点(性质2)
  • 从得到的这个 e 结点 e结点 e结点 出发,离 u 结点 u结点 u结点 最远的 s 结点 s结点 s结点,一定是该树直径的其中另一个端点(性质2,定义)
  • s 结点 s结点 s结点 e 结点 e结点 e结点 就是这棵树直径的端点
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int> head(N,-1);
struct Edge{                                        //链式前向星int to,next;int weight;Edge():to(-1), next(-1){}				        //初始化为无邻居节点
} edge[N<<1];
int n;                                              //人有n个,关系有n-1条
int cot=0;
vector<int> dis(N,0);                               //记录距离
void Add_Edge(int u, int v,int w){edge[cot].to=v;edge[cot].weight=w;edge[cot].next=head[u];                         //记录 上一个邻居节点 的 存储编号head[u]=cot++;                                  //当前 邻居节点 的 存储编号,以便下一个邻居节点的访问
}
void DFS(int u, int father, int w){dis[u]=dis[father]+w;                           //更新当前节点的距离for(int i=head[u]; ~i; i=edge[i].next){		    //遍历cur节点的邻居节点[~i相当于i=-1]int v=edge[i].to;                           //v 是 u 的子节点if(v==father)       continue;               //不遍历父节点DFS(v, u, edge[i].weight);}
} 
int main(void){int n;      cin>>n;for(int i=1; i<n; i++){int u,v,w;    cin>> u >> v >> w;Add_Edge(u,v,w);  Add_Edge(v,u,w);          //无向 记录 双向有向}/*找到树直径的其中一个端点*/DFS(1,0,0);                                     //以 1号节点 为根节点遍历整个树,获得所有节点离节点1的距离int n1_id=1;                                    //初始化for(int i=1; i<=n; i++)                         //遍历输入的n个结点if(dis[i]>dis[n1_id])                       //最终是为了找到到 结点1 距离最远的那个节点n1_id=i;/*找到树直径的另一端点*/DFS(n1_id,0,0);                                 //以 n1_id结点 为根节点开始遍历整棵树,最终最远的那个距离就是直径int n2_id=1;                                    //初始化for(int i=1; i<=n; i++)                         //遍历输入的n个结点if(dis[i]>dis[n2_id])                       //最终是为了找到到 n1_id节点 距离最远的那个节点n2_id=i;cout<<dis[n2_id];       return 0;
}
//法一
void DFS(int u, int father, int w){dis[u]=dis[father]+w;                           //更新当前节点的距离for(int i=head[u]; ~i; i=edge[i].next){			//遍历cur节点的邻居节点[~i相当于i=-1]int v=edge[i].to;                           //v 是 u 的子节点if(v==father)       continue;               //不遍历父节点DFS(v, u, edge[i].weight);}
} //法二
vector<bool> visit(N,false);
void DFS(int u, int father, int w){dis[u]=dis[father]+w;                           //更新当前节点的距离visit[u]=true;								    //标记为已访问,避免下次再访问for(int i=head[u]; ~i; i=edge[i].next){int v=edge[i].to;                           //v 是 u 的子节点if(visit[v])        continue;               //v已经算过了,避免重复遍历DFS(v, u, edge[i].weight);}
}  
DFS(BFS)为何不能用在有 负权值 的树里呢?

很容易想到一个反例:离目标节点 的 倒数第二远的节点到最远的节点 这条边如果权值为负,会得出 dis[倒数第二远]>dis[最远的节点] 的错误结论。(是因为我们让权值和作为判断 是否远 的依据)

而我们比较depth,就可以解决这个问题。

21-1-5 法二:树形DP(动态规划)

直通车——>DP算法求最大子序和
为何 DP能解决 有 负权值 的树 的树直径问题?
  • 贪心思想 实现的 DFS算法 暴露的问题就是只满足 “局部最优,而不顾全局”Dijkstra算法 同理也不能使用在有 负权值 的树。

  • 全局最优的 DP动态规划算法可以弥补这个短板,Floyd算法 基于 DP 同理也能使用在有 负权值 的树。

如何实现 动态规划?

d p [ u ] dp[u] dp[u] 是 以 u结点 为根节点的子树上,从 u结点 出发能到达的最远路径的长度,这个路径的终点是 u结点子树 的叶子节点

  • 状态转移方程

d p [ u ] = m a x ( d p [ v i ] + e d g e [ u , v i ] ) dp[u]=max(dp[v_i]+edge[u,v_i]) dp[u]=max(dp[vi]+edge[u,vi]) v i v_i vi u 结点 u结点 u结点 第 i 个邻居节点, e d g e [ u , v i ] edge[u,v_i] edge[u,vi] 是他们边上的权值】

  • 每个结点的最长路径长度

u 节点 u节点 u节点 的每个结点的最长路径长度 记录在 a n s [ u ] ans[u] ans[u]

f [ u ] f[u] f[u] 状态转换方程: f [ u ] = m a x ( d p [ u ] + d p [ v i ] + e d g e [ u , v i ] ) f[u]=max(dp[u]+dp[v_i]+edge[u,v_i]) f[u]=max(dp[u]+dp[vi]+edge[u,vi])【此时的 d p [ u ] dp[u] dp[u] 是不包含 v i 子树 v_i子树 vi子树 的,即 d p [ u ] = m a x ( d p [ v i ] + e d g e [ u , v i ] ) dp[u]=max(dp[v_i]+edge[u,v_i]) dp[u]=max(dp[vi]+edge[u,vi]) 是在这个状态转化方程后执行的】

maxlen=max(maxlen, dp[u]+dp[v]+edge[i].weight);
dp[u]=max(dp[u], dp[v]+edge[i].weight);
//注这里的 max函数 可以替换成 三目运算符 来实现

树的直径为 m a x l e n = m a x ( f [ u ] ) maxlen=max(f[u]) maxlen=max(f[u]),即 最大的 结点的最长路径长度(从定义出发考虑)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int> head(N,-1);
struct Edge{                                        //链式前向星int to,next;int weight;Edge():to(-1), next(-1){}				        //初始化为无邻居节点
} edge[N<<1];
int n;                                              //人有n个,关系有n-1条
int maxlen=0;                                       //树的直径
int cot=0;
vector<bool> visit(N,false);
vector<int> dp(N,0);
void Add_Edge(int u, int v,int w){edge[cot].to=v;edge[cot].weight=w;edge[cot].next=head[u];                         //记录 上一个邻居节点 的 存储编号head[u]=cot++;                                  //当前 邻居节点 的 存储编号,以便下一个邻居节点的访问
}
void DP(int u){visit[u]=true;                                  //标记为已访问,避免下次再访问for(int i=head[u]; ~i; i=edge[i].next){         //遍历cur节点的邻居节点[~i相当于i=-1]int v=edge[i].to;                           //v 是 u 的子节点int u_v=edge[i].weight;                     //u 与 v 边上的权值if(visit[v])       continue;                //v已经算过了,避免重复遍历DP(v);maxlen=max(maxlen, dp[u]+dp[v]+u_v);        //将当前值与历史最大比较dp[u]=max(dp[u], dp[v]+u_v); }
}
int main(void){int n;      cin>>n;for(int i=1; i<n; i++){int u,v,w;    cin>> u >> v >> w;Add_Edge(u,v,w);  Add_Edge(v,u,w);          //无向 记录 双向有向}DP(1);cout<<maxlen;       return 0;
}

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

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

相关文章

点云从入门到精通技术详解100篇-基于激光点云语义的环境感知

目录 前言 环境感知传感器研究现状 单一激光雷达应用 激光雷达与相机融合的应用

社区团购新零售搭伙拼团小程序源码(前后端)

社区团购新零售搭伙拼团小程序源码是一款非常实用的电商小程序&#xff0c;它包含了前后端文件&#xff0c; 可以快速地进行部署和使用。该小程序是基于微信小程序开发的&#xff0c;支持社区团购、新零售、搭伙拼团等多种功能。 该小程序具有良好的用户体验&#xff0c;包括…

数据湖在爱奇艺数据中台的应用

01 我们眼中的数据湖 作为爱奇艺的数据中台团队&#xff0c;我们的核心任务是管理和服务公司内的大量数据资产。在实施数据治理的过程中&#xff0c;我们不断吸收新的理念&#xff0c;引入尖端的工具&#xff0c;以精细化我们的数据体系管理。“数据湖”作为近年来数据领域广泛…

链表入门(单链表讲)

链表 1.链表1.1 链表概念及其结构1.2 链表的分类 2.单链表代码实现2.1 单链表的定义2.2 单链表的初始化2.3 单链表的新增结点2.4 单链表的打印2.4 单链表的插入2.4.1 头插2.4.2 尾插2.4.3 任意位置插入 2.5 单链表的删除2.5.1 头删2.5.2 尾删2.5.3 任意位置删除 2.6 单链表的查…

【PickerView案例10-国旗选择界面02 Objective-C预言】

一、好了,我们继续来实现这个国旗选择界面: 1.它的界面里面,是不是很简单,就一个UIPickerView,就完事儿了 然后,显示的每一行内容呢, 1)一个文字Label 2)一个图片 那大家应该有意识,它返回的应该是一个View,对吧, 代理方法里面,有一个返回View的,viewForRow…

Django:五、登录界面实现动态图片验证码

一、下载包 pip install pillow 二、代码 这是一个函数&#xff0c;无输入&#xff0c;返回两个值。一个值是图片&#xff0c;一个值是图片中的数字及字母。 需要注意&#xff1a;font_fileMonaco.ttf 是一个验证码字体文件&#xff0c;如有需要&#xff0c;可三连私信。 …

【逆向】在程序空白区添加Shellcode

目录 硬编码 内存对齐和文件对齐 节表 实战 滴水逆向03-17 #include <windows.h>LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) …

Pycharm 搭建 Django 项目,看完这一篇就够了

1. 安装需求 在使用 python 框架 Django 需要注意下面事项 Pycharm 版本是专业版而不是社区版本Pycharm 配置好了 python 解释器 &#xff08;一般我们现在用的都是python3&#xff09;我自己使用的是 Pycharm 版本是2020.1.2 2. 准备工作 2.1 新建项目 首先我们打开 Pycharm …

Linux离线安装elasticsearch|header|kibna插件最详细

1.准备软件安装包 [hadoophost152 elasticsearch]$ ll -rw-r--r--. 1 hadoop hadoop 515807354 9月 23 23:40 elasticsearch-8.1.1-linux-x86_64.tar.gz -rw-r--r--. 1 hadoop hadoop 1295593 9月 23 23:48 elasticsearch-head-master.tar.gz -rw-r--r--. 1 hadoop hadoop…

记录一个 GUI 库的对比测试结果

1&#xff0c;Java 的 JavaFX 2&#xff0c;golang 的 Fyne 1, Java 测试的是一个俄罗斯方块的 GUI 程序。一切正常。 2&#xff0c;Golang github 的原仓库网络问题&#xff0c;没能测试上&#xff0c;使用以下库 https://gitee.com/mirrors/Fyne 下载代码后提示“编译失…

Tomcat 与 JDK 对应版本关系

对应关系 Tomcat版本 jdk版本11.0.x JDK 21及以后10.1.x JDK11及以后10.0.xJDK1.8及以后9.0.x JDK1.8及以后8.5.xJDK1.7及以后8.0.x JDK1.7及以后 查看对应关系方法&#xff1a; 登陆Tomcat官网&#xff1a;Apache Tomcat - Welcome! 结果&#xff1a;

Picgo

title: “Picgo” createTime: 2021-07-01T12:05:5808:00 updateTime: 2021-07-01T12:05:5808:00 draft: false author: “name” tags: [“未标签”] categories: [“未分类”] description: “测试的” picgo 地址 : /home/zhu/software/node/node-v14.15.0-linux-x64/bin文…

河北吉力宝以步力宝健康鞋引发的全新生活生态商

在当今瞬息万变的商业世界中&#xff0c;成功企业通常都是那些不拘泥于传统、勇于创新的先锋之选。河北吉力宝正是这样一家企业&#xff0c;通过打造一双步力宝健康鞋&#xff0c;他们以功能性智能科技穿戴品为核心&#xff0c;成功创造了一种结合智能康养与时尚潮流的独特产品…

C#读取CAD文件(dwg/dxf)并处理

文章目录 一、前言二、调研过程2.1 CAD相关2.2 DWG2.3 DXF2.4 小结三、解析CAD的库3.1 netDxf3.2 Teigha3.2.1 官网获取3.2.2 网上获取四、图形计算一、前言 需求: 项目要求识别CAD图纸(图纸内容与现实事物比例是1:1)中的内容,并提取出一些关键信息。 这里的CAD图纸是指C…

unity 实用框架

单例模式基类 类不继承mono的单例基类 /// <summary> /// 单例基类 /// </summary> //泛型解决&#xff0c;给他一个约束要么是这个类本身要么是它的子类 public class SingleBase<T>where T : SingleBase<T> {protected SingleBase() { }//线程锁。…

C++ 类和对象(4)构造函数

C的目标之一是让使用类对象就像使用标准类型一样&#xff0c;但是常规的初始化语法不适用于类似类型Stock&#xff1a; int year 2001&#xff1b; struct thing {char * pn;int m; }; thing amabob {"wodget",-23}; //有效初始化 Stock hot {"Sukies Autos…

Leetcode 992. K 个不同整数的子数组

文章目录 题目代码&#xff08;9.27 首刷看解析&#xff09; 题目 Leetcode 992. K 个不同整数的子数组 代码&#xff08;9.27 首刷看解析&#xff09; 滑动窗口&#xff0c;恰好转换为&#xff1a;最多K个不同的数 - 最多K-1个不同的数 class Solution { public:int subarr…

kafka伪集群部署,使用KRAFT模式

1:拉去管理kafka界面UI镜像 docker pull provectuslabs/kafka-ui2:拉去管理kafka镜像 docker pull bitnami/kafka3:docker-compose.yml version: 3.8 services:kafka-1:container_name: kafka1image: bitnami/kafka ports:- "19092:19092"- "19093:19093&quo…

测试用例的八大基本准则

测试用例的八大基本准则 测试用例的八大基本准则功能测试性能测试兼容性测试安全测试可靠性测试易用性测试数据库测试接口测试 测试案例 测试用例的八大基本准则 上节测试用例的设计中我们讨论如何设计一个测试用例&#xff0c;知道了测试用例的设计有&#xff1a;“边界值&am…

Linux命令(87)之pwd

linux命令之pwd 1.pwd介绍 linux命令pwd(全称&#xff1a;print working directory)用来列出当前目录 2.pwd用法 pwd [参数] pwd参数(了解即可&#xff0c;99.99%用不到) 参数说明-L显示逻辑路径-P显示实际物理路径 3.实例 3.1.显示当前目录 命令&#xff1a; pwd [ro…