树形DP(树形背包+换根DP)

树形DP

  1. 没有上司的舞会

家常便饭了,写了好几遍,没啥好说的,正常独立集问题。

int head[B];
int cnt;
struct node
{int v,nxt;
}e[B<<1];
void modify(int u,int v)
{e[++cnt].nxt=head[u];e[cnt].v=v;head[u]=cnt;
}
int a[B];
int f[B][5];
int n;
void dfs(int u,int pre)
{for (int i=head[u];i;i=e[i].nxt){int v=e[i].v;if (v==pre) continue;dfs(v,u);f[u][1]+=f[v][0];f[u][0]+=max(f[v][0],f[v][1]);}
}
void work()
{cin>>n;for (int i=1;i<=n;i++) f[i][1]=read();for (int i=1;i<n;i++){int v=read(),u=read();modify(u,v);modify(v,u); }dfs(1,0);cout<<max(f[1][0],f[1][1]);
}
  1. 选课

树上背包问题。

说说自己犯的错误

  • 式子推得太慢
  • 滚动数组特别容易记混状态,尤其是背包,因为是从大到小更新,如果在枚举到大数的时候发生转移却用的小的,那么更新的答案必然是错误的,这个问题我调了一下午,就是滚动数组的错误,一定切记,对于逆序的滚动数组问题,一定好想好当前所需状态是否已经更新,而不是借用上一维的状态,这种错误最容易犯在背包上!!
  • 这个题目优化点在于,用一个虚拟点来连接森林,这样就不用再写一遍了
#include<bits/stdc++.h>
using namespace std;
int read(){int x;scanf("%d",&x);return x;}
const int B=1e6+10;
const int inf=0x3f3f3f3f;
int T;
int n,m;
int f[309][309];
int dp[309][309];
int a[B];
int head[B];
int cnt;
struct node
{int v,nxt;
}e[B<<1];
void modify(int u,int v)
{e[++cnt].nxt=head[u];e[cnt].v=v;head[u]=cnt;
}
int fa[B];
int vis[B];
void dfs(int u,int pre)
{f[u][1]=a[u];for (int i=head[u];i;i=e[i].nxt){int v=e[i].v;if (v==pre) continue;dfs(v,u);}for (int i=head[u];i;i=e[i].nxt){int v=e[i].v;if (v==pre) continue; for (int j=m;j>=1;j--){for (int k=1;k<=j;k++)dp[u][j]=max(dp[u][j],dp[u][j-k]+f[v][k]);//f[u][j]=dp[u][j-1]+a[u];//因为我们的j是倒序枚举,先更新大的在更新小的,那么dp[u][j-1]并不是当前已经对v//点进行操作完毕的 dp,而是上一维度 }for (int j=1;j<=m;j++) f[u][j]=dp[u][j-1]+a[u];//放在外面就是正确的,这是因为用的是当前维度下的dp,滚动数组容易反的错误 }
} 
int ans[B];
int num;
int F[B];
void work()
{cin>>n>>m;for (int i=1;i<=n;i++){fa[i]=read();a[i]=read();modify(fa[i],i);modify(i,fa[i]);}for (int i=1;i<=n;i++){if (!fa[i]){dfs(i,0);ans[++num]=i;}}for (int i=1;i<=num;i++){for (int j=m;j>=1;j--){int x=ans[i];for (int k=0;k<=min(m,j);k++)F[j]=max(F[j],F[j-k]+f[x][k]);}	}cout<<F[m]; 
}
int main()
{T=1;while (T--) work();return 0;
}
  1. 积蓄程度

题面:见ACwing287

思路

换根DP的常规思路

  • 有下往上递推一遍
  • 再由上往下递推一遍

我们先来想如果根节点固定的时候如何做,比较容易的DP,其实也就是递推,没有任何决策问题。

d [ u ] d[u] d[u] 表示以 u u u 为根节点的最小流量。通过题目可以知道,一条路径上的流量取决于最小流量边,所以这是一个维护最小值的问题。

转移有
d [ u ] = ∑ min ⁡ { d [ v ] , w } d[u]=\sum \min\{d[v],w\} d[u]=min{d[v],w}
再来考虑如何换根。

先来考虑由根节点换到儿子会发生什么变化

图

我们会发现, d [ 1 ] d[1] d[1] 中需要去掉 d [ 2 ] d[2] d[2] 做出的贡献,即 d [ 1 ] − min ⁡ { d [ 2 ] , w } d[1]-\min\{d[2],w\} d[1]min{d[2],w}

d [ 2 ] d[2] d[2] 需要加入 d [ 1 ] d[1] d[1] 的贡献,即 d [ 2 ] + min ⁡ { d [ 1 ] , w } d[2]+\min\{d[1],w\} d[2]+min{d[1],w}

我来思考,如果是在树中间换根怎么办,我们发现,父亲节点还需要同时加入父亲父亲的节点情况。

在这里插入图片描述

比如绿色部分,可是这条边比较难表示,不容易找到 w w w,如果我们看成 2 2 2 节点就是根节点,不再是父亲节点的时候,此时的值刚好就是 2 2 2 当根的值,我们用 f [ 2 ] f[2] f[2] 表示 2 2 2 为根的最大值。那么在转移的时候我们就不需要考虑由父亲父亲的情况。转移由
f [ v ] = d [ v ] + min ⁡ { w , f [ u ] − min ⁡ { w , d [ v ] } } f[v]=d[v]+\min\{w,f[u]-\min\{w,d[v]\}\} f[v]=d[v]+min{w,f[u]min{w,d[v]}}

对于度数只为1的还需要特别注意,度数唯一不一定是叶子结点,也可能是根节点,当换根时,根节点变成叶子结点, f [ u ] − min ⁡ { w , d [ v ] } f[u]-\min\{w,d[v]\} f[u]min{w,d[v]} 变成 0 0 0 ,实际上应该直接加入边值 w w w,所以需要特别判断。

换根时候的注意点

  • 必须一直保持根和儿子换,而不是父亲和儿子换。
  • 根节点和叶子结点要注意
#include<bits/stdc++.h>
using namespace std;
int read(){int x;scanf("%d",&x);return x;}
const int B=1e6+10;
const int inf=0x3f3f3f3f;
int T;
int n;
int head[B],cnt;
struct node
{int v,nxt,w;
}e[B<<1];
void modify(int u,int v,int w)
{e[++cnt].nxt=head[u];e[cnt].v=v;e[cnt].w=w;head[u]=cnt;
}
int f[B];
int d[B];
int du[B];
void dfs1(int u,int pre)
{for (int i=head[u];i;i=e[i].nxt){int v=e[i].v;int w=e[i].w;if (v==pre) continue;dfs1(v,u);if (du[v]!=1) d[u]+=min(d[v],w);else d[u]+=w;}
} 
void dfs2(int u,int pre)
{for (int i=head[u];i;i=e[i].nxt){int w=e[i].w;int v=e[i].v;if (v==pre) continue;if (du[u]==1) f[v]=d[v]+w;else f[v]=d[v]+min(w,f[u]-min(d[v],w));dfs2(v,u);}
}
void work()
{cin>>n;cnt=0;memset(head,0,sizeof(head));for (int i=1;i<=n;i++){f[i]=0;d[i]=0;du[i]=0;}for (int i=1;i<n;i++){int u=read(),v=read(),w=read();modify(u,v,w); du[u]++; du[v]++;modify(v,u,w);}dfs1(1,0); f[1]=d[1];dfs2(1,0);int ans=0;for (int i=1;i<=n;i++) ans=max(ans,f[i]);cout<<ans<<"\n";
}
int main()
{T=read();while (T--) work();return 0;
}
/*
1
3
1 2 1
2 3 1
*/ 

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

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

相关文章

REACT--组件通信

组件之间如何进行通信&#xff1f; 组件通信 组件的通信主要借助props传递值 分为整体接收、解构接收 整体接收 import PropTypes from prop-types;//子组件 function Welcome(props){return (<div>hello Welcome,{props.count},{props.msg}</div>) }// 对 We…

【排序算法】六大比较类排序算法——插入排序、选择排序、冒泡排序、希尔排序、快速排序、归并排序【详解】

文章目录 六大比较类排序算法&#xff08;插入排序、选择排序、冒泡排序、希尔排序、快速排序、归并排序&#xff09;前言1. 插入排序算法描述代码示例算法分析 2. 选择排序算法描述优化代码示例算法分析 3. 冒泡排序算法描述代码示例算法分析与插入排序对比 4. 希尔排序算法描…

纠错检索增广生成论文

一、摘要 动机&#xff1a;RAG严重依赖于检索文档的相关性&#xff0c;如果检索出错&#xff0c;那么LLM的输出结果也会出现问题 解决方案&#xff1a;提出纠正性检索增强生成&#xff08;CRAG&#xff09;即设计一个轻量级的检索评估器&#xff0c;用来评估针对某个查询检索…

Java NIO与传统IO性能对比分析

Java NIO与传统IO性能对比分析 在Java中&#xff0c;I/O&#xff08;输入输出&#xff09;操作是开发中最常见的任务之一。传统的I/O方式基于阻塞模型&#xff0c;而Java NIO&#xff08;New I/O&#xff09;引入了非阻塞和基于通道&#xff08;Channel&#xff09;和缓冲区&a…

easelog(1)基础C++日志功能实现

EaseLog(1)基础C日志功能实现 Author: Once Day Date: 2025年2月22日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 注&#xff1a;本简易日志组件代码实现参考了Google …

Vue面试2

1.跨域问题以及如何解决跨域 跨域问题&#xff08;Cross-Origin Resource Sharing, CORS&#xff09;是指在浏览器中&#xff0c;当一个资源试图从一个不同的源请求另一个资源时所遇到的限制。这种限制是浏览器为了保护用户安全而实施的一种同源策略&#xff08;Same-origin p…

MongoDB应用设计调优

应用范式设计 什么是范式 数据库范式概念是数据库技术的基本理论&#xff0c;几乎是伴随着数据库软件产品的推出而产生的。在传统关系型数据库领域&#xff0c;应用开发中遵循范式是最基本的要求。但随着互联网行业的发展&#xff0c;NoSQL开始变得非常流行&#xff0c;在许多…

Mac安装配置Tomcat 8

一、官网下载 Index of /disthttps://archive.apache.org/dist/ 1、进入界面如下&#xff1a; 2、我们找到Tomcat文件夹并进入 3、找到Tomcat 8并打开 4、找到对应的版本打开 5、打开bin 6、找到“apache-tomcat-8.5.99.tar.gz”并下载 二、配置Tomcat 1、解压已经下载好的…

【论文精读】VLM-AD:通过视觉-语言模型监督实现端到端自动驾驶

论文地址&#xff1a; VLM-AD: End-to-End Autonomous Driving through Vision-Language Model Supervision 摘要 人类驾驶员依赖常识推理来应对复杂多变的真实世界驾驶场景。现有的端到端&#xff08;E2E&#xff09;自动驾驶&#xff08;AD&#xff09;模型通常被优化以模仿…

百度搜索,能否将DeepSeek变成“内功”?

最近&#xff0c;所有的云平台和主流APP都在努力接入DeepSeek。其中&#xff0c;搜索类APP与搜索引擎更是“战况激烈”。那么问题来了&#xff0c;接入DeepSeek已经变成了标准配置&#xff0c;到底应该如何做出差异化&#xff1f;接入DeepSeek这件事能不能实现11大于2的效果&am…

Flask实现高效日志记录模块

目录 一. 简介&#xff1a; 1. 为什么需要请求日志 二. 日志模块组成 1. 对应日志表创建&#xff08;包含日志记录的关键字段&#xff09; 2. 编写日志记录静态方法 3. 在Flask中捕获请求日志 4. 捕获异常并记录错误日志 5. 编写日志接口数据展示 6. 写入数据展…

25轻化工程研究生复试面试问题汇总 轻化工程专业知识问题很全! 轻化工程复试全流程攻略 轻化工程考研复试真题汇总

轻化工程复试心里没谱&#xff1f;学姐带你玩转面试准备&#xff01; 是不是总觉得老师会问些刁钻问题&#xff1f;别焦虑&#xff01;其实轻化工程复试套路就那些&#xff0c;看完这篇攻略直接掌握复试通关密码&#xff01;文中有重点面试题可直接背~ 目录 一、这些行为赶紧避…

查看已经安装的Python库,高效管理自己的Python开发环境

在日常的Python开发中&#xff0c;掌握如何管理和查看已经安装的库是非常重要的。这不仅能帮助你了解当前项目的依赖关系&#xff0c;还能避免出现版本冲突等问题。在这篇文章中&#xff0c;我们将详细介绍查看已安装Python库的方法&#xff0c;并提供一些实用的工具和技巧。 …

Selenium实战案例1:论文pdf自动下载

在上一篇文章中&#xff0c;我们介绍了Selenium的基础用法和一些常见技巧。今天&#xff0c;我们将通过中国科学&#xff1a;信息科学网站内当前目录论文下载这一实战案例来进一步展示Selenium的web自动化流程。 目录 中国科学&#xff1a;信息科学当期目录论文下载 1.网页内…

Visual Studio Code 2025 安装与高效配置教程

一、软件简介与下载 1. Visual Studio Code 是什么&#xff1f; Visual Studio Code&#xff08;简称VS Code&#xff09;是微软推出的免费开源代码编辑器&#xff0c;支持 智能代码补全、Git集成、插件扩展 等功能&#xff0c;适用于前端开发、Python、Java等多种编程场景。…

工业路由器和工业交换机,打造高效稳定的工业网络?

工业路由器和工业交换机各有千秋&#xff0c;但如何将它们完美结合&#xff0c;构建稳定高效的工业网络&#xff1f;答案就在这里&#xff01; 工业物联网&#xff08;IIoT&#xff09;是高效、稳定的工业网络成为智慧工厂、工业自动化和远程监控等场景的基础支撑。工业路由器…

DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

TSMaster【第七篇:千机百变——面板设计艺术】

武侠场景导入&#xff1a;唐门暗器阁的启示 江湖传言&#xff0c;唐门暗器之所以独步天下&#xff0c;全凭其「千机匣」中七十二种机关变化。TSMaster面板设计恰似打造暗器机关——控件如同飞镖、机簧、毒针&#xff0c;组合方式不同则威力迥异。昔日某新势力车型因仪表盘刷新…

提升 AI 服务的稳定性:Higress AI 网关的降级功能介绍

在使用 LLM 服务时&#xff0c;服务的稳定性和可用性至关重要。然而&#xff0c;由于网络问题、服务器故障或其他不可控因素&#xff0c;LLM 服务可能会暂时不可用。为了保障用户体验和业务连续性&#xff0c;Higress AI 网关提供了强大的模型降级和令牌降级功能。本文将介绍这…

提升C++项目编译速度

目录 一、问题背景 二、代码规范方面的解决方案 2.1 拆分头文件 2.2 拆分巨型类 2.3 使用前置声明 2.4 避免在头文件中包含实现 2.5 避免头文件重复包含 2.6 将常用且变动较少的独立到一个文件 三、代码业务重构方面经验 3.1 使用PIMPL&#xff08;Pointer to Imple…