[洛谷-P1272] 重建道路(树形背包DP)

news2024/11/29 3:56:59

[洛谷-P1272] 重建道路(树形背包DP)

  • 一、题目
  • 重建道路
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
      • 样例解释
      • 限制与约定
  • 二、思路
    • 1、状态表示
    • 2、转移方程
    • 3、循环设计
    • 4、初末状态
  • 三、代码

一、题目

重建道路

题目描述

一场可怕的地震后,人们用 N N N 个牲口棚(编号 1 ∼ N 1\sim N 1N)重建了农夫 John 的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

John 想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有 P P P 个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

输入格式

第一行两个整数, N N N P P P

第二行到第 n n n 行,每行两个整数 I I I J J J,表示节点 I I I 是节点 J J J 的父节点。牧场运输系统可以被构建成一棵以 1 号节点为根的树

输出格式

单独一行,包含一旦被破坏将分离出恰含 P P P 个节点的子树的道路的最小数目。

样例 #1

样例输入 #1

11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11

样例输出 #1

2

提示

样例解释

如果道路 1 − 4 1-4 14 1 − 5 1-5 15 被破坏,含有节点( 1 , 2 , 3 , 6 , 7 , 8 1,2,3,6,7,8 1,2,3,6,7,8)的子树将被分离出来。

限制与约定

1 ≤ N ≤ 150 1\le N\le 150 1N150 1 ≤ P ≤ N 1\le P\le N 1PN,保证给出的是一棵树。

二、思路

这道题简单的来说就是:
给出一棵含有 n 个节点的有根树,我们现在希望通过删除一些边,让他有一棵有 m 个节点的新树,求删除的最少边的数量。

我们需要注意的有一下几点:
第一,我们选择的点必须构成一棵树,即如果我们选择了一个点,那么就必须选择这个点的父节点。 从这里我们就能够发现这道题其实是一道树形背包DP的问题。

剩下的注意点将在DP的分析中给出。

1、状态表示

f [ u ] [ j ] f[u][j] f[u][j]表示在以 u u u为根节点的树中选择 j j j个点,删除的最小边的数量。

2、转移方程

f [ u ] [ s i z ( u ) ] [ j ] = m i n ( f [ u ] [ s i z ( u ) ] [ j ] , f [ u ] [ s i z ( u ) − 1 ] [ j − k ] + f [ s o n ] [ s i z ( s o n ) ] [ k ] − 2 ) f[u][siz(u)][j]=min(f[u][siz(u)][j], f[u][siz(u)-1][j-k]+f[son][siz(son)][k]-2) f[u][siz(u)][j]=min(f[u][siz(u)][j],f[u][siz(u)1][jk]+f[son][siz(son)][k]2)

这里的 s o n son son指的是 u u u的子节点。 s i z ( u ) siz(u) siz(u)指的是 u u u的子树个数。 s i z ( s o n ) siz(son) siz(son)指的是其子树的子树个树。那么上述的转移方程可以描述为:

在以 u u u为根的子树中选 j j j个节点的最小代价等于在前 s i z ( u ) − 1 siz(u)-1 siz(u)1个子树中选 j − k j-k jk个,在第 s i z ( u ) siz(u) siz(u)个子树 s o n son son中选 k k k个的最小代价。

我们为什么要减 2 2 2呢?

我们在初始化的时候,我们是切除了和子节点之间相连的边,同时也切除了和父节点相连的边。
在这里插入图片描述
A是B的父节点,所以在算红色的联通块的时候,我们删除了一次A和B之间的边,B又是A的子节点,所以在算紫色连通块的时候,我们又删除了一次A和B之间的边。但实际上我们是需要在A和B之间连接一条边的。也就是说我们不应该删除这条边。我们的 f [ i ] [ j ] f[i][j] f[i][j]记录的是删除掉的边。所以我们应该-2。

这里我们还需要注意一个关键点:根据刚刚的分析,我们想要选择子树中的点就必须选择这个点所对的根节点。即如果我们总共想选择 j j j个点,那么我们在子树中至多选 j − 1 j-1 j1个。这个即我们 k k k的范围。

而上述三维的DP是比较容易爆空间的,所以可以优化为两维:
f [ u ] [ j ] = m i n ( f [ u ] [ j ] , f [ u ] [ j − k ] + f [ s o n ] [ k ] − 2 ) f[u][j]=min(f[u][j], f[u][j-k]+f[son][k]-2) f[u][j]=min(f[u][j],f[u][jk]+f[son][k]2)

3、循环设计

最外层肯定是去遍历树的节点,利用子节点去更新父节点,所以我们最外层的循环其实就是DFS。

那么第二层循环就是我们的选择的点的个数 j j j。但是由于我们优化掉了一维,所以我们需要倒序枚举。

第三层即去枚举在子树 s o n son son中选择的个数。

4、初末状态

f [ u ] [ 1 ] f[u][1] f[u][1]表示的是在以 u u u为根的树中保留一个点所需要的最小代价,即保留 u u u点本身。
那么我们只需要剪掉和 u u u直接相连的子节点即可剪去整棵子树,但是不要忘记和节点 u u u相连的父节点也要减去!

f [ u ] [ 1 ] = s i z ( u ) + 1 f[u][1]=siz(u) + 1 f[u][1]=siz(u)+1,其余初始化正无穷 I N F INF INF即可。

但是这里有一个特殊情况,如果我们的 u u u是整棵树的祖宗节点 r o o t root root。而祖宗节点是没有父节点的,所以我们不需要减去那个和父节点相连的边,此时我们只需要初始化为: f [ r o o t ] [ 1 ] = s i z ( r o o t ) f[root][1]=siz(root) f[root][1]=siz(root)。这是这道题的另外一个坑点。

末状态是 f [ r o o t ] [ p ] f[root][p] f[root][p],这个想法对吗?
显然是不对的。因为题目中只说保留 p p p个,并没有说保留的是以祖宗节点 r o o t root root为根。所以我们需要去枚举所有的 f [ i ] [ p ] f[i][p] f[i][p],找出一个最小值。

三、代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define endl '\n'
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 300 + 10;
int f[N][N];
int siz[N];
int root = 0;
vector<int>edge[N];
int n, m;
void dp(int u, int M)
{
	f[u][1] = edge[u].size() + (u != root);
	
	for(int i = 0; i < edge[u].size(); i ++ )
	{
		int son = edge[u][i];
		dp(son, M);
		for(int j = M; j >= 1; j -- )
			for(int q = 0; q <= (j - 1); q ++ )
				f[u][j] = min(f[u][j - q] + f[son][q] - 2, f[u][j]);
	}
}
void solve()
{
	cin >> n >> m;
	memset(f, INF, sizeof f);
	for(int i = 0; i < n - 1; i ++ )
	{
		int a, b;
		cin >> a >> b;
		if(root == b || root == 0)
			root = a;
		edge[a].push_back(b);
	}
	
	dp(root, m);

	int ans = f[root][m];

	for(int i = 1; i <= n; i ++ )
	{
		if(f[i][m] < ans)
			ans = f[i][m];
	}
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	solve();
}

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

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

相关文章

计算机Ping命令使用详解

计算机Ping命令使用详解 在网络中 ping 是一个十分强大的 TCP/IP 工具。它的作用主要为: 1、用来检测网络的连通情况和分析网络速度 2、根据域名得到服务器 IP 3、根据 ping 返回的 TTL 值来判断对方所使用的操作系统及数据包经过路由器数量。 我们通常会用它来直接 ping ip 地…

uni-app api 获取系统信息(高、宽)用法及封装

uni-app提供了异步(uni.getSystemInfo)和同步(uni.getSystemInfoSync)的2个API获取系统信息 uniapp 官网解析地址 uni.getSystemInfo 异步获取系统信息 参数名类型必填说明successFunction是接口调用成功的回调failFunction否接口调用失败的回调函数completeFunction否接口…

腾讯混元AI大模型训练技术揭秘——太极AngelPTM

编者按&#xff1a;秉承“技术提效”理念&#xff0c;腾讯广告不断探索技术能力边界&#xff0c;全面升级广告系统&#xff0c;基于“一大平台、两大模型”持续精进创新研发&#xff0c;提升投放效率与投放效果&#xff0c;助力广告主实现高效的全域经营与生意增长。本篇文章是…

请介绍类加载过程,什么是双亲委派模型?

第23讲 | 请介绍类加载过程&#xff0c;什么是双亲委派模型&#xff1f; Java 通过引入字节码和 JVM 机制&#xff0c;提供了强大的跨平台能力&#xff0c;理解 Java 的类加载机制是深入 Java 开发的必要条件&#xff0c;也是个面试考察热点。 今天我要问你的问题是&#xff0…

ESP32驱动-红外寻迹传感器驱动

红外寻迹传感器驱动 1、红外寻迹传感器介绍 红外寻迹传感器具有一对红外线发射管与接收管,发射管发射出一定频率的红外线,当检测方向遇到障碍物(反射面)时,红外线反射回来被接收管接收,经过比较器电路处理之后,输出接口会输出一个数字信号(低电平或高电平,取决于电路…

JVM篇之内存及GC

目录一、JVM内存区域1.1程序计数器1.2虚拟机栈1.3本地方法栈1.4堆1.5方法区二、JVM运行时内存2.1新生代(轻量级GC)2.2老年代&#xff08;重量级GC&#xff09;一、JVM内存区域 JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法栈】、线程共享区域【JAVA 堆、…

蓝桥杯嵌入式PWM_IN(打开中断)

1.原理图 2.配置 3.代码 关键函数 HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1) HAL_TIM_IC_CaptureCallback(TIM_HandTypeDef *htim)//回调函数 HAL_TIM_GET_COUNTER(&htim3) __HAL_TIM_SetCounter(&htim3,0)void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef …

spring5源码篇(4)——beanFactoryPostProcessor执行/注解bean的装配

spring-framework 版本&#xff1a;v5.3.19 前面研究了beanDefinition的注册&#xff0c;但也仅仅是注册这一动作。那么在spring容器启动的过程中&#xff0c;是何时/如何装配的&#xff1f;以及装配的bean是如何注入的&#xff1f; &#xff08;考虑到xml方式基本不用了以及篇…

探秘MySQL——全面了解索引、索引优化规则

文章目录0.什么是索引1.常用索引分类逻辑维度底层数据结构维度物理维度&#xff08;InnoDB&#xff09;2.为什么底层是B树平衡二叉查找树红黑树B树&#xff08;多叉&#xff09;B树&#xff08;多叉&#xff09;3.MySQL索引优化SQL性能分析之explainQ.MySQL如何查看查询是否用到…

fastdfs提高系统连接数

1.操作系统 vi /etc/systemd/system.conf 修改后重启系统。 ulimit -n 验证是否生效。 2.tracker ①docker exec -it trackerID bin/bash ②vi /etc/fdfs/tracker.conf 一般性能测试场景可配置10000 3.storage ①docker exec -it storageID bin/bash ②vi /etc/fdfs/st…

k8s-Pod域名学习总结

k8s-Pod域名学习总结 大纲 k8s内置DNS服务 配置Pod的域名服务 CornDNS配置 默认Pod的域名 自定义Pod的域名 实战需求 1 Pod有自己的域名 2 集群内部的Pod可以通过域名访问其他的Pod 基础准备&#xff1a; 1 k8s 集群版本1.17 k8s内置DNS服务 k8s1.17安装完成后自动创建…

保姆级使用PyTorch训练与评估自己的MixMIM网络教程

文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址&#xff1a;https://github.com/Fafa-DL/Awesome-Backbones 操作教程&#xff1a;https://www.bilibili.co…

【python】如何用canvas在自己设计的软件上作画

文章目录前言Canvas组件Canvas画布界面画长方体画多边形PhotoImage组件展示gif的图片展示gif法2总结前言 python学习之路任重而道远&#xff0c;要想学完说容易也容易&#xff0c;说难也难。 很多人说python最好学了&#xff0c;但扪心自问&#xff0c;你会用python做什么了&a…

数据结构——线性数据结构(C语言实现顺序表详解)

1.什么线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串等… 在讲顺序表之前&#xff0c;我们先大致了解一下线性表。…

qt .pro文件 qmake编译过程

#&#xff1a;注释一行 QT&#xff1a;此项目中使用的Qt modules列表 CONFIG&#xff1a;此项目中使用的配置选项 TARGET&#xff1a;目标输出文件的名字 TEMPLATE&#xff1a;当生成二进制文件时项目的模版&#xff0c;例如app,lib 平台下使用 windows { SOURCES SysInf…

剑指 Offer II 024. 反转链表

题目链接 剑指 Offer II 024. 反转链表 easy 题目描述 给定单链表的头节点 head&#xff0c;请反转链表&#xff0c;并返回反转后的链表的头节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;h…

不用费劲,这5款效率工具为你解决学习工作烦恼

今天我要向大家推荐5款超级好用的效率软件&#xff0c;无论是在学习还是办公中都能够极大地提高效率。这些软件可以帮助你解决许多问题&#xff0c;而且每个都是真正的神器。 1.键盘仿真鼠标——NeatMouse NeatMouse 是一个小型的工具能够使用鼠标光标控制指针。当你的鼠标不…

python编程基础

python编程基础 1、什么是编程语言&#xff1f; 编程语言是用来控制计算机的一系列指令&#xff08;Instruction&#xff09;&#xff0c;它有固定的格式和词汇&#xff08;不同编程语言的格式和词汇不一样&#xff09;&#xff0c;必须遵守&#xff0c;否则就会出错&#xf…

opencv加水印

本文介绍opencv给图片加水印的方法。 目录1、添加水印1.1、铺满1.2、在指定区域添加1.3、一比一铺满1、添加水印 添加水印的原理是调低两张图片的透明度&#xff0c;然后叠加起来。公式如下&#xff1a; dst src1 * opacity src2 * (1 - opacity) gamma; opacity是透明度&a…

UE官方教程笔记02-实时渲染基础下

对官方教程视频[官方培训]02-实时渲染基础下 | 陈拓 Epic的笔记没听懂的地方就瞎写反射实时渲染中反射是一个非常有挑战的特性UE中有多种不同的方案&#xff0c;各有各的优势和缺点反射捕获屏幕空间反射平面反射LumenRT Reflection反射捕获在指定位置捕获一张Cube Map需要预计算…