【算法】换根DP

news2025/2/5 8:58:27

文章目录

  • 什么是换根DP
  • 例题分析——P3478 [POI2008] STA-Station
  • 题目列表1
    • 834. 树中距离之和⭐⭐⭐⭐⭐(两次 dfs)
      • 思路——冷静分析,列出式子
      • 算法分析⭐⭐⭐⭐⭐
    • 310. 最小高度树⭐⭐⭐⭐⭐
    • 2581. 统计可能的树根数目⭐⭐⭐⭐⭐
    • C. Bear and Tree Jumps
    • 287. 积蓄程度
  • 题目列表2
    • Atcoder Educational DP Contest, Problem V, Subtree
    • Educational Codeforces Round 67, Problem E, Tree Painting
    • POJ 3585 Accumulation Degree
    • [USACO10MAR]Great Cow Gathering G
    • CodeForce 708C Centroids
  • 相关链接

什么是换根DP

https://oi-wiki.org/dp/tree/#%E6%8D%A2%E6%A0%B9-dp

树形 DP 中的换根 DP 问题又被称为二次扫描,通常不会指定根结点,并且根结点的变化会对一些值,例如子结点深度和、点权和等产生影响。

通常需要两次 DFS,第一次 DFS 预处理诸如深度,点权和之类的信息,在第二次 DFS 开始运行换根动态规划。

例题分析——P3478 [POI2008] STA-Station

https://www.luogu.com.cn/problem/P3478
在这里插入图片描述

题目列表1

834. 树中距离之和⭐⭐⭐⭐⭐(两次 dfs)

834. 树中距离之和

在这里插入图片描述

思路——冷静分析,列出式子

https://leetcode.cn/problems/sum-of-distances-in-tree/solutions/103325/c-liang-ci-dfsde-dao-da-an-by-congwang357-2/

将问题拆分:对于两个相邻节点A和B,将树拆分为两个子树,根节点分别为A和B,A节点到其他所有节点的距离和 ans(A) = A子树中所有节点到A节点的距离和sum(A) + B子树中所有节点到B节点的距离和sum(B) + B子树的大小cnt(B);
同理,ans(B) = sum(B) + sum(A) + cnt(A);

由此我们得到: ans(A) = sum(A) + sum(B) + cnt(B); ans(B) = sum(B) + sum(A) + cnt(A);
则,两个相邻接点的解之间的关系为:ans(A) = ans(B) - cnt(A) + cnt(B) = ans(B) - cnt(A) + (N - cnt(A));

因此,对于根节点root的任意子节点child,ans(child) = ans(root) - cnt(child) + N - cnt(child);

class Solution {
    List<Integer>[] g;
    int[] ans, size;    // size是各个节点作为根节点的子树大小
    int n;
    public int[] sumOfDistancesInTree(int n, int[][] edges) {
        g = new ArrayList[n];
        Arrays.setAll(g, e -> new ArrayList<Integer>());
        for (int[] edge: edges) {
            int x = edge[0], y = edge[1];
            g[x].add(y);
            g[y].add(x);
        }
        this.n = n;
        ans = new int[n];
        size = new int[n];
        Arrays.fill(size, 1);

        dfs(0, -1, 0);
        reroot(0, -1);
        return ans;
    }

    // 求ans[0]和各个size[i]
    void dfs(int x, int fa, int depth) {
        ans[0] += depth;                    // depth 是 0 到 x 的距离
        for (int y: g[x]) {
            if (y != fa) {
                dfs(y, x, depth + 1);
                size[x] += size[y];         // 累加 x 的儿子 y 的子树大小
            }
        }
    }

    // 求答案
    void reroot(int x, int fa) {
        for (int y: g[x]) {
            if (y != fa) {
                ans[y] = ans[x] + n - 2 * size[y];
                reroot(y, x);
            }
        }
    }
}

算法分析⭐⭐⭐⭐⭐

https://leetcode.cn/problems/sum-of-distances-in-tree/solutions/2345592/tu-jie-yi-zhang-tu-miao-dong-huan-gen-dp-6bgb/
在这里插入图片描述
我们得到了重要公式:
a n s [ y ] = a n s [ x ] + n − 2 ∗ s i z e [ y ] ans[y] = ans[x] + n - 2 * size[y] ans[y]=ans[x]+n2size[y]

如何理解?
y 和以 y为根的子树的距离相比 x 与 以 y为根的子树的距离 少了 cnt[y]
除了 以 y为根的子树,剩下的节点数量是 n - cnt[y],这些和 y 的距离相比 和 x 的距离多了 n - cnt[y]

因此:ans[y] = ans[x] + n - 2 * size[y]

310. 最小高度树⭐⭐⭐⭐⭐

https://leetcode.cn/problems/minimum-height-trees/description/
在这里插入图片描述
提示:

1 <= n <= 2 * 10^4
edges.length == n - 1
0 <= ai, bi < n
ai != bi
所有 (ai, bi) 互不相同
给定的输入 保证 是一棵树,并且 不会有重复的边

主要参考 :https://leetcode.cn/problems/minimum-height-trees/solutions/1397830/c-huan-gen-by-vclip-sa84/ 编写的代码。

class Solution {
    List<Integer> ans = new ArrayList<Integer>();
    List<Integer>[] g;
    int n;
    int[] ds, ds2;

    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        this.n = n;
        ds = new int[n];
        ds2 = new int[n];
        // 建树
        g = new ArrayList[n];
        Arrays.setAll(g, e -> new ArrayList());
        for (int[] edge: edges) {
            int x = edge[0], y = edge[1];
            g[x].add(y);
            g[y].add(x);
        }

        dfs(0, -1);     // 求各个节点为根的子树高度(以0节点为最根节点)
        dfs2(0, -1);    // 换根dp

        // 求答案
        int h = n;
        for (int i = 0; i < n; ++i) {
            if (ds2[i] < h) {
                h = ds2[i];
                ans.clear();
            }
            if (ds2[i] == h) ans.add(i);
        }
        return ans;
    }

    // 计算以0号节点为根的树中,以各个节点为根的子树高
    public int dfs(int x, int fa) {
        for (int y: g[x]) {
            if (y != fa) {
                ds[x] = Math.max(ds[x], dfs(y, x) + 1);
            }
        }
        return ds[x];
    }

    // 进行换根动态规划,计算出所有的树高
    public void dfs2(int x, int fa) {
        // 计算 x 的子树高的最大值和次大值
        int first = -1, second = -1;    // 默认是没有子树
        for (int y: g[x]) {
            if (ds[y] > first) {
                second = first;
                first = ds[y];
            } else if (ds[y] > second) second = ds[y];
        }
        ds2[x] = first + 1;             // 计算出 x 为根的树高
        // 进行换根,计算x作为以y为根节点的树的子树时的高度
        for (int y: g[x]) {             
            if (y != fa) {
                ds[x] = (ds[y] != first? first: second) + 1;
                dfs2(y, x);
            }
        }
    }
}

2581. 统计可能的树根数目⭐⭐⭐⭐⭐

https://leetcode.cn/problems/count-number-of-possible-root-nodes/

在这里插入图片描述
提示:

edges.length == n - 1
2 <= n <= 10^5
1 <= guesses.length <= 10^5
0 <= ai, bi, uj, vj <= n - 1
ai != bi
uj != vj
edges 表示一棵有效的树。
guesses[j] 是树中的一条边。
guesses 是唯一的。
0 <= k <= guesses.length

在这里插入代码片

C. Bear and Tree Jumps

https://codeforces.com/problemset/problem/771/C

在这里插入图片描述

在这里插入代码片

287. 积蓄程度

https://www.acwing.com/problem/content/289/
在这里插入图片描述

题目列表2

Atcoder Educational DP Contest, Problem V, Subtree

https://atcoder.jp/contests/dp/tasks/dp_v

Educational Codeforces Round 67, Problem E, Tree Painting

https://codeforces.com/contest/1187/problem/E

POJ 3585 Accumulation Degree

http://poj.org/problem?id=3585

[USACO10MAR]Great Cow Gathering G

https://www.luogu.com.cn/problem/P2986

CodeForce 708C Centroids

https://codeforces.com/problemset/problem/708/C

相关链接

关于基础树形DP可见:
【算法】树形DP ①(树的直径)
【算法】树形DP ② 打家劫舍Ⅲ(树上最大独立集)

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

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

相关文章

Coggle 30 Days of ML(23年7月)打卡

前言 最近开始关注LLM相关知识&#xff0c;但之前的NLP范式的技能不能丢。 这个练习还是比较适合我&#xff0c;感谢举办方选题&#xff0c;快速全部打卡一波。 打卡记录 任务一: 报名比赛&#xff0c;下载比赛数据集并完成读取 比赛链接&#xff1a;https://challenge.xfy…

第十六章:Understanding Convolution for Semantic Segmentation——理解用于语义分割的卷积

0.摘要 最近深度学习特别是深度卷积神经网络&#xff08;CNN&#xff09;的进展&#xff0c;显著提高了之前语义分割系统的性能。在这里&#xff0c;我们展示了通过操作与卷积相关的操作来改进逐像素的语义分割&#xff0c;这些操作在理论和实践上都具有价值。首先&#xff0c;…

【Java动态代理】—— 每天一点小知识

&#x1f4a7; J a v a 动态代理 \color{#FF1493}{Java动态代理} Java动态代理&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&am…

PyTorch 深度学习处理多维特征的输入

import numpy as np import torch import matplotlib.pyplot as plt# prepare dataset xy np.loadtxt(diabetes.csv, delimiter,, dtypenp.float32) x_data torch.from_numpy(xy[:, :-1]) # 第一个‘&#xff1a;’是指读取所有行&#xff0c;第二个‘&#xff1a;’是指从第…

Linux常用命令——eject命令

在线Linux命令查询工具 eject 用来退出抽取式设备 补充说明 eject命令用来退出抽取式设备。若设备已挂入&#xff0c;则eject命令会先将该设备卸除再退出。 eject允许可移动介质&#xff08;典型是cd-ROM、软盘、磁带、或者JAZ以及zip磁盘&#xff09;在软件控制下弹出。该…

Visual Studio 2022打包exe ,自动按日期生成文件

echo offREM 获取当前的日期和时间 set YEAR%DATE:~0,4% set MONTH%DATE:~5,2% set DAY%DATE:~8,2% set HOUR%TIME:~0,2% set MINUTE%TIME:~3,2% set SECOND%TIME:~6,2%REM 获取原始文件名 set "FilePath$(TargetPath)" for %%F in ("%FilePath%") do (set…

第46节:cesium 水面效果(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><!

【AT89C52单片机项目】99累减器

实验目的 掌握STC89C52RC单片机最小系统构成&#xff0c;最小系统由单片机芯片、时钟电路及复位电路组成。 掌握STC89C52RC单片机开发板与数码管的原理图、控制方式。 掌握对单片机I/O的复杂控制 熟练掌握C语言的设计和调试方法。 实验仪器 一套STC89C52RC开发板套件&…

Linux C/C++实现Socks5代理及Socks5协议抓包分析

如果你想在保持匿名的同时以更好的安全性和性能浏览网页&#xff0c;SOCKS5代理是一个不错的选择。 在使用互联网时&#xff0c;存在许多安全和数据隐私风险。此外&#xff0c;您可能不得不面对一些限制。想象一下&#xff0c;你想访问一个网站&#xff0c;但你根本无法访问它&…

【MySQL】从执行计划了解MySQL优化策略

文章目录 前言一、什么是执行计划1.1. 使用EXPLAIN命令1.2. 使用PROFILING 二、执行计划生成过程三、执行计划的操作符3.1. 查询计划操作符3.2. 连接操作符3.3. 辅助操作符 四、执行计划的诊断分析4.1. 使用EXPLAIN命令4.2. 检查索引4.3. 分析查询日志 五、如何分析 EXPLAIN 结…

pytorch 欠拟合和过拟合 多项式回归

欠拟合 训练误差和验证误差都有&#xff0c;还可能比较严重&#xff0c; 但它们之间仅有差距不大。 这个时候模型不能降低训练的误差&#xff0c;有可能是我们设计的模型简单了&#xff0c;表达能力不足&#xff0c; 捕获试图学习的模式比较难。由于我们的训练和验证误差之间的…

java——this、封装、static修饰成员变量、成员方法

目录 ☂️this的用法 1.访问当前对象的成员变量 2.访问当前对象的成员方法 3.调用当前对象的其他构造方法来简化代码 ☂️封装 什么是封装&#xff1f; 访问修饰限定符 ☂️static修饰成员变量 ☂️static修饰成员方法 ☂️this的用法 1.访问当前对象的成员变量 我们…

容器运行时

容器运行时 Docker运行时Containerd运行时CRI 版本支持Containerd安装配置 systemd cgroup 驱动 CRI-O运行时Mirantis 容器运行时 目前k8s支持的几个常用的运行时 containerdCRI-ODocker EngineMirantis Container Runtime v1.24 之前的 Kubernetes 版本直接集成了 Docker Engi…

IDEA+spring boot+activiti+shiro+layui+Mysql权限管理系统源码

IDEAspring bootactivitishirolayuiMysql权限管理系统 一、系统介绍1.环境配置 二、系统展示1. 管理员登录2.主页3.用户管理4.部门管理5. 菜单管理6. 角色管理7. 字典管理8.定时任务9.操作日志10.生成管理 三、部分代码UserMapper.javaUserController.javaUser.java 四、其他获…

Python Flask构建微信小程序订餐系统 (七)

🔥 账号管理显示状态 🔥 账号管理选择状态后并显示 修改前 修改后 运行效果 🔥 账号管理执行账号的删除 🔥 修改前 修改后 这里使用的图标库 font awesome</

引入Vue的方式

1.cdn引入 <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…

竞赛管理系统实现自动化测试

目录 一、为竞赛管理系统设计测试用例 二、根据测试用例进行测试 登录页面测试 功能测试 界面测试 用户注册页面 功能测试 界面测试 竞赛信息列表页面测试 界面测试 添加竞赛信息页面测试 功能测试 界面测试 修改竞赛信息页面测试 功能测试 一、为竞赛管理…

Rollup 的作用、使用教程、支持 Vue、以及与 Webpack / Babel 的区别

一、前置知识 没接触过前端模块化概念的同学可先参考&#xff1a;JS & Node 模块化解释&#xff1a;AMD、UMD、CommonJS、 ESM 二、什么是 Rollup &#xff1f; Rollup 工具可以将代码转成不同模块&#xff0c;实现一套代码多端&#xff08;浏览器/Node&#xff09;引入…

SpringBoot的静态资源文件访问问题

一、采用默认的文件存放位置&#xff1a; Spring Boot 对静态资源映射提供了默认配置&#xff0c; 默认将 /** 所有访问映射到以下目录&#xff1a; classpath:/static classpath:/public classpath:/resources classpath:/META-INF/resources在如上路径的文件可以被直接访问…

Havoc!新的开源命令和控制C2框架

工具介绍 Havoc是一款现代化可扩展的后渗透命令控制框架&#xff0c;可作为Cobalt Strike和Brute Ratel等付费选项的替代方案。 关注【Hack分享吧】公众号&#xff0c;回复关键字【230618】获取下载链接 Havoc包含各种各样的模块&#xff0c;允许渗透测试人员&#xff08;和黑…