【学习笔记】详解换根法(换根DP)

一.换根DP的概念

1.换根DP是什么?

换根DP,又叫二次扫描,是树形DP的一种。

2.换根DP能解决什么问题?

换根DP能解决不指定根结点,并且根节点的变化会对一些值产生影响的问题。例如子结点深度和、点权和等。如果要 暴力求解出最优解,则我们可以枚举所有的节点为根,然后分别跑一次搜索,这样的时间复杂度会达到 O(n^{2}),显然不可接受。这时可以考虑使用换根DP解决。

3.换根DP与一般的树形DP相比有什么不同?

其相比于一般的树形DP具有以下特点:

  • 树上的不同点作为根,其解不同
  • 为其求解答案,不能单求某点的信息,需要求解每个节点的信息
  • 无法通过一次搜索完成答案的求解,因为一次搜索只能得到一个节点的答案。

二.换根DP的解法

换根dp通常会与树形dp 结合,我们可以先任定一个根节点root,通过树形dp的思想计算出一个答案。但这样求解出的答案只是以root为根的解,并不能确定是不是最优解。于是再考虑当root的子节点作为根节点时,答案怎样变化。一般可以在 O(1) 的时间复杂度内完成答案的转化。这样整道题的时间复杂度就由 O(n^{2}) 降为了 O(n)

总结一下,换根DP总共是三个步骤:

  1. 指定某个节点为根节点
  2. 第一次搜索完成预处理(如子树大小等),同时得到以上述节点为根的解
  3. 第二次搜索进行换根的动态规划,由已知解的节点推出相连节点的解。

如果到了这里还没有听懂的话,不用慌,可以结合下面的例题慢慢理解。

三.例题精讲

1.STA-Station

question

sol

我们先来画一下样例~(这里先假设以1号节点为根)

在这幅图中,siz[i]表示以i节点为根的子树中节点个数(包括i节点本身),而deep[i]表示i在以1号节点为根时的深度(1号节点深度为1)。

注意:代码中其实不需要开deep数组(第一次dfs时可以直接在dfs设置一个变量dep,然后每次递归时直接将以一号节点为根的解加上dep就行了),但是为了方便解释,我还是画上了。

这样,我们就可以得出来以1为根时深度之和(也就是解)为1+2+3+3+3+4+5+5=26。

然后我们就可以进行换根操作(比如此时以1的子节点4号节点为根)

siz我们依然沿用第一次得到的结果,deep我为了方便演示已经更新了。

从中我们可以发现一些规律。这棵树中从前(以1号节点为根时)属于4的子树的节点(2,3,4,5,6,7,8)的深度都减了1,而其他的节点(这里样例中只有1)的深度都加了1。大家可以自行在纸上多画几个换根后的图,加深理解。

归纳一下我们得到的结论:

如果此时要将根节点换成节点x

那么,以x为根时的解就是:

以x的父节点为根的子树的节点数-以x为根的子树的节点数+(总节点数-以点为根的子树的节点数)

              ↑                                                     ↑                                               ↑

以x为根时的解的基数     这些节点的深度都要-1(在这里省略了乘1)     其他的节点的深度都要+1

假设dh[x]代表以x为根时的解,fa为x的父节点

dh[x] = dh[fa] - siz[x] + (n - siz[x])

总结一下:

  • 我们假设此时将根节点换成节点x,则其子树由两部分构成,第一部分是其原子树,第二部分则是1号节点的其他子树
  • 根从1号节点变为节点x的过程中,我们可以发现第一部分的深度降低了1第二部分的深度则上升了1,而这两部分节点的数量在第一次搜索时就得到了(siz数组)。
  • DP公式:dh[x] = dh[fa] - siz[x] + (n - siz[x])

最后还要注意:第二次dfs后我们还要枚举dh[1~n]取其中的最大值才能得到答案!

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
vector<int> vec[2000001];
int n,u,v,siz[2000001],dh[2000001],ans;
void dfs(int x,int fa,int dep)
{
  siz[x] = 1;//因为以x节点为根的子树包括x自身
  dh[1] += dep;//求以1节点为根的解
  for(int i = 0; i < vec[x].size(); i++)
  {
    int son = vec[x][i];
    if(son != fa)
    {
      dfs(son,x,dep + 1);
      siz[x] += siz[son];//不断累加子树中的节点个数
    }
  }
}
void dfs_2(int x,int fa)
{
  if(x != 1) dh[x] = dh[fa] - siz[x] + (n - siz[x]);//DP公式
  for(int i = 0; i < vec[x].size(); i++)
  {
    int son = vec[x][i];
    if(son != fa) dfs_2(son,x);
  }
}
signed main()
{
  cin>>n;
  for(int i = 1; i < n; i++)
  {
    cin>>u>>v;
    vec[u].push_back(v);//建树
    vec[v].push_back(u);
  }
  dfs(1,0,1);
  dfs_2(1,0);
  for(int i = 1; i <= n; i++) ans = max(ans,dh[i]);//取以各个节点为根的解的最大值
  cout<<ans;
  return 0;
}

2.[USACO10MAR] Great Cow Gathering G

question

sol

这道题与例题基本是一致的,只是添加了每个节点和每条边的权值,在DP时注意加上与乘上即可,没有什么大的变形。 如果实在不会,可以结合下面的代码理解。

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct ff
{
  int len,id;
};
vector<ff> vec[100001];
int n,u,v,siz[1000001],dh[1000001],ans,w,c[1000001],s;
void dfs(int x,int fa)
{
  siz[x] = c[x];//以x为根的1子树是包括x节点本身的,所以siz[x]要初始化成x节点上的奶牛个数
  for(int i = 0; i < vec[x].size(); i++)
  {
    int son = vec[x][i].id;
    if(son != fa)
    {
      dfs(son,x);
      siz[x] += siz[son];//不断累加以子节点为根的子树上的奶牛数
      dh[1] += siz[son] * vec[x][i].len;//因为题目中添加了边权和点权,故不能像sta那题一样直接+dep
    }
  }
}
void dfs_2(int x,int fa)
{
  for(int i = 0; i < vec[x].size(); i++)
  {
    int son = vec[x][i].id;
    if(son != fa)
    {
      dh[son] = dh[x] - siz[son] * vec[x][i].len + (s - siz[son]) * vec[x][i].len;//此处可以自行画图推导一下
      dfs_2(son,x);
    }
  }
}
signed main()
{
  cin>>n;
  for(int i = 1; i <= n; i++)
  {
    cin>>c[i];
    s += c[i];//s:奶牛的总数
  }
  for(int i = 1; i < n; i++)
  {
    cin>>u>>v>>w;
    vec[u].push_back({w,v});//邻接表建双向边
    vec[v].push_back({w,u});
  }
  dfs(1,0);
  dfs_2(1,0);
  ans = 1e18;
  for(int i = 1; i <= n; i++) ans = min(ans,dh[i]);//因为要求深度最小,所以是取解的最小值
  cout<<ans;
  return 0;
}

四.结语

如果您觉得这篇文章不错,就请点赞关注支持一下吖!ヾ(•ω•`)o

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

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

相关文章

使用JDBC连接mysql

JDBC:Java DataBase Connectivity,Java数据库连接。 使用Java语言操作关系型数据库的一套API。 原理&#xff1a;官方&#xff08;sun公司&#xff09;定义出一套操作所有关系型数据库的规则&#xff0c;即接口&#xff1b;所有的数据库厂商去实现这套接口&#xff0c;提供数据…

[word] word小数点对齐怎么设置 #微信#其他#其他

word小数点对齐怎么设置 使用Word编辑文档的时候&#xff0c;如果有小技巧的话&#xff0c;可以解决很多遇到的问题&#xff0c;也让工作更高效的完成&#xff0c;下面给大家分享word小数点对齐怎么设置的小技巧。 1、设置格式 选中内容&#xff0c;点击段落一一制表符&#…

2024.2.3 寒假训练记录(17)

补一下牛客&#xff0c;菜得发昏了&#xff0c;F搞了两个小时都没搞出来&#xff0c;不如去开H了 还没补完 剩下的打了atc再来 文章目录 牛客 寒假集训1A DFS搜索牛客 寒假集训1B 关鸡牛客 寒假集训1C 按闹分配牛客 寒假集训1D 数组成鸡牛客 寒假集训1E 本题又主要考察了贪心牛…

react 使用react-seamless-scroll实现无缝滚动

文章目录 1. 实现无缝滚动效果2. react-seamless-scroll 无缝滚动案例介绍3. react 项目集成3.1 项目引入 cssSeamlessScroll 滚动组件3.2 完整代码3.2.1 newBet.tsx 代码3.2.2 index.module.scss 1. 实现无缝滚动效果 实现单步向下滚动点击更多展开&#xff0c;收起&#xff0…

Python爬虫urllib详解

前言 学习爬虫&#xff0c;最初的操作便是模拟浏览器向服务器发出请求&#xff0c;那么我们需要从哪个地方做起呢&#xff1f;请求需要我们自己来构造吗&#xff1f;需要关心请求这个数据结构的实现吗&#xff1f;需要了解 HTTP、TCP、IP 层的网络传输通信吗&#xff1f;需要知…

Java八大常用排序算法

1冒泡排序 对于冒泡排序相信我们都比较熟悉了&#xff0c;其核心思想就是相邻元素两两比较&#xff0c;把较大的元素放到后面&#xff0c;在一轮比较完成之后&#xff0c;最大的元素就位于最后一个位置了&#xff0c;就好像是气泡&#xff0c;慢慢的浮出了水面一样 Jave 实现 …

RabbitMQ_00000

MQ的相关概念 RabbitMQ官网地址&#xff1a;https://www.rabbitmq.com RabbitMQ API地址&#xff1a;https://rabbitmq.github.io/rabbitmq-java-client/api/current/ 什么是MQ&#xff1f; MQ(message queue)本质是个队列&#xff0c;FIFO先入先出&#xff0c;只不过队列中…

Jmeter 基于Docker 实现分布式测试

基于Docker 实现分布式测试 制作Jmeter基础镜像制作工作节点镜像启动工作节点启动控制节点遇到的问题 使用Docker 部署Jmeter非常方便&#xff0c;可以省略软件的安装以及配置&#xff0c;比如jdk、jmeter。需要部署多个工作节点可以节省时间。 制作Jmeter基础镜像 下载jmeter…

Kubernetes集群搭建

一、概述 Kubernetes是一个Google开源的全新的分布式容器集群管理系统&#xff0c;由于从第一个字母到字母s中间有8个字母&#xff0c;所以简称K8s。 二、准备 ip角色内存192.168.187.130master4G192.168.187.131node2G192.168.187.132node2G 小提示&#xff1a; 设置静态i…

【课程作业_01】国科大2023模式识别与机器学习实践作业

国科大2023模式识别与机器学习实践作业 作业内容 从四类方法中选三类方法&#xff0c;从选定的每类方法中 &#xff0c;各选一种具体的方法&#xff0c;从给定的数据集中选一 个数据集&#xff08;MNIST&#xff0c;CIFAR-10&#xff0c;电信用户流失数据集 &#xff09;对这…

TCP TIME_WAIT 过多怎么处理

文章目录 1.什么是 TCP TIME_WAIT&#xff1f;2.为什么要 TIME_WAIT?3.TIME_WAIT 过多的影响4.解决办法4.1 调整短连接为长连接4.2 调整系统内核参数 5.小结参考文献 1.什么是 TCP TIME_WAIT&#xff1f; TCP 断开连接四次挥手过程中&#xff0c;主动断开连接的一方&#xff…

ctfshow——文件包含

文章目录 web 78——php伪协议第一种方法——php://input第二种方法——data://text/plain第三种方法——远程包含&#xff08;http://协议&#xff09; web 78——str_replace过滤字符php第一种方法——远程包含&#xff08;http://协议&#xff09;第二种方法——data://&…

游戏被DDOS攻击无法访问时该如何处理

游戏行业随着时代的发展有着突飞猛进的变化&#xff0c;尤其是互联网时代智能手机的普及&#xff0c;让游戏行业发展上了一个新的台阶。因为游戏带来的巨大利润&#xff0c;游戏行业一直是DDoS攻击的首选目标。 DDoS是Distributed Denial of Service的缩写&#xff0c;即分布式…

学习Android的第二天

目录 Android User Interface 用户界面 UI Android View与ViewGroup的概念 Android View android.view.View android.view.View XML 属性 android:id 属性 Android ViewGroup android.view.ViewGroup ViewGroup.LayoutParams ViewGroup.MarginLayoutParams ViewGr…

Redis核心技术与实战【学习笔记】 - 19.Pika:基于SSD实现大容量“Redis”

前言 随着业务数据的增加&#xff08;比如电商业务中&#xff0c;随着用户规模和商品数量的增加&#xff09;&#xff0c;就需要 Redis 能保存更多的数据。你可能会想到使用 Redis 切片集群&#xff0c;把数据分散保存到不同的实例上。但是这样做的话&#xff0c;如果要保存的…

java社区养老年人服务系统springboot+vue

为了帮助用户更好的了解和理解程序的开发流程与相关内容&#xff0c;本文将通过六个章节进行内容阐述。 第一章&#xff1a;描述了程序的开发背景&#xff0c;程序运用于现实生活的目的与意义&#xff0c;以及程序文档的结构安排信息&#xff1b; 第二章&#xff1a;描述了程序…

uniapp 高德地图显示

1. uniapp 高德地图显示 使用前需到**高德开放平台&#xff08;https://lbs.amap.com/&#xff09;**创建应用并申请Key   登录 高德开放平台&#xff0c;进入“控制台”&#xff0c;如果没有注册账号请先根据页面提示注册账号   打开 “应用管理” -> “我的应用”页面…

Leetcode 85. 最大矩形

题目信息 LeetoCode地址: 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目理解 该题是84题的升级版。84题给出了一个一维数组&#xff0c;即一行数据&#xff0c;每个元素是高度。而该题则是给出了二维数组&#xff0c;只需我们将每一行的高度…

太强了,AI数字人从制作到变现一次搞定

AI数字人从制作到变现 如果说GPT类大模型是我们人类的第二大脑&#xff0c;数字人就是我们人类在互联网上的第二个身体。随着 AI 的迅速发展&#xff0c;2024 年 AI 模型开始从大型语言模型向大型视觉模型转变。数字人技术作为其分支之一&#xff0c;正日益成为科技、娱乐、教…

【GAMES101】Lecture 14 15 辐射度量学

目录 辐射度量学 Radiant flux&#xff08;光通量&#xff09; intensity&#xff08;发光强度&#xff09; irradiance radiance 辐射度量学 主要讲述了物理学中的Basic radiometry (辐射度量学)&#xff0c;就是我们在之前的计算光照中没有用具体的物理单位去衡量和描述…