IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> BZOJ 2133 切割(树形DP,树上背包)大概是本题全网第一篇题解 >_<【BZOJ 修复工程】 -> 正文阅读

[数据结构与算法]BZOJ 2133 切割(树形DP,树上背包)大概是本题全网第一篇题解 >_<【BZOJ 修复工程】

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


BZOJ 2133 切割这道题全网搜不到任何一篇题解 >_<

看评测记录也没有几个人AC…

不过其实很简单就是了,可能是大佬们写完之后不屑于写题解吧 o(〃^▽^〃)o


题目链接

https://hydro.ac/d/bzoj/p/2133

hydro 的 BZOJ 修复工程 !(我也去领了一点题慢慢修着玩,这题就是我修的嘿嘿嘿)

题目描述

有一棵 n n n 个节点的无根树,节点编号为 1 … n 1\dots n 1n。每个节点有一个非负权值。现在需要把这棵树分成若干个联通块(不能有剩下的点),每个联通块的节点个数在 k 1 k_1 k1? k 2 k_2 k2? 之间,每个联通块的得分就是该联通块中所有节点权值的平均值。总得分就是指对这个树切割后所有联通块的得分的和。你需要求出一种分割的方案使得总得分尽量小。

输入格式

第一行三个整数 n , k 1 , k 2 n,k_1,k_2 n,k1?,k2?

第二行 n n n 个整数,第 i i i 个数表示编号为 i i i 的节点的权值,每两个相邻整数之间用一个空格隔开;

接下来 n ? 1 n-1 n?1 行,每行两个整数 x , y x,y x,y ,表示编号为 x x x的节点和编号为 y y y的节点之间有一条边。

输出格式

如果存在一个切割方案,那么输出一个实数(四舍五入保留二位小数),表示所有分割方案中最小的总得分。如果不存在切割方案,那么输出所有节点权值和的两倍。如果对于某个测试点你的输出和标准输出完全一样,那么将得到该测试点全部的分,否则该测试点不得分(我写的sp貌似有问题,大家可以使用 double 类型存储数据,输出结果的整数部分保证不超过 9 9 9 位(十进制),使用 double 类型可以保证小数部分至少是 6 6 6位,因而精度应该不存在问题)。

输入样例

3 1 1
1 1 1
1 2
2 3

输出样例

3.00 

数据规模与约定

对于 100 % 100\% 100% 的数据, 1 ≤ k 1 ≤ k 2 ≤ n , 2 ≤ n ≤ 50 1\le k_1\le k_2\le n,2\le n\le 50 1k1?k2?n2n50,每个点的权值均不超过 1 0 9 10^9 109

Solution

首先随便定一个点 r o o t root root 为树根,将无根树转化为有根树。

数据较大,显然不可能直接爆搜,考虑树形DP。

显然设 f [ i ] f[i] f[i] 表示以 i i i 为根的子树中能获得的最小的总得分,分析题意考虑需要维护哪些信息。

首先题目中给定的计算方式暂时没有什么切入点,先忽略掉,对于每个连通块,块内结点个数是有一个范围限制的,即 k 1 ≤ n u m ≤ k 2 k_1\le num\le k_2 k1?numk2?,显然我们需要维护这个信息,考虑设 f [ i , j ] f[i,j] f[i,j] 表示以 i i i 为根的子树在切割后,所属的连通块的结点个数为 j j j ,能获得的最小总得分。

发现没法转移…

考虑 n ≤ 50 n\le 50 n50 O ( n 4 ) ~ O ( n 4 log ? n ) O(n^4)\sim O(n^4\log n) O(n4)O(n4logn),甚至 O ( n 5 ) O(n^5) O(n5) 都有可能通过(手动狗头)

考虑再增加一些维护的信息。

树形DP考虑从子树的角度出发分析,题目可以转换为从子树中选择若干个点,加入到根节点的连通块内,即树上背包问题,考虑转移和需要维护的信息。显然我们只能从子树中选取不超过该连通块的总数 j j j 的节点数放入当前连通块内,考虑维护 k k k 表示根节点 i i i 的子树中被选入 i i i 所在的连通块的结点个数,这样我们将问题转化为一个 0/1 背包的模型,直接按照背包 O ( n 2 ) O(n^2) O(n2) 转移即可。

即设 f [ i , j , k ] f[i,j,k] f[i,j,k] 表示以 i i i 为根的 子树 在切割后,结点 i i i 所属的连通块的结点个数为 j j j ,子树中被选入 i i i 所在的连通块的结点个数为 k k k (显然包括),能获得的最小的总得分。

考虑如何维护答案,因为我们并不能确定每个连通块内结点的个数,因此我们直接以 i i i 为根维护答案即可,设 a n s [ i ] ans[i] ans[i] 表示以 i i i 为根的 子树中 的最小总得分,显然我们必须要从子树中选择 j j j 个结点填满背包才算合法,故答案为: a n s [ i ] = min ? { f [ i ] [ j ] [ j ] ∣ j ∈ [ k 1 , k 2 ] } \displaystyle ans[i] = \min\{f[i][j][j]\mid j\in[k_1,k_2]\} ans[i]=min{f[i][j][j]j[k1?,k2?]}

最后的答案显然是 a n s [ r o o t ] ans[root] ans[root]

最后如果没有合法的方案的话(ans[root] == maxx),根据题意输出总和的二倍即可。

注意 double 类型的数使用 memset 初始化的时候, ∞ \infin 使用 0x7f ? ∞ -\infin ? 使用 0xef

Code

#include <bits/stdc++.h>
using namespace std;

const int maxn = 100 + 7, maxm = maxn << 1, INF = 0x3f3f3f3f;
const double DINF = 1e18, eps = 1e-8;
int n, m, s, t;
int k1, k2;
double w[maxn];
double f[maxn][maxn][maxn];
double ans[maxn];
int siz[maxn];
vector <int> e[maxn];
bool vis[maxn];
double sum;

int sgn(double x)
{
	if(fabs(x) < eps)
		return 0;
	if(x < 0)
		return -1;
	return 1;
}
 
void dfs(int x, int fa)
{ 
	for (auto y : e[x]) {
		if(y == fa) continue;
		dfs(y, x); 
	} 
	for (int j = k1; j <= k2; ++ j) {
		f[x][j][1] = w[x] / j;
		for (auto y : e[x]) {
			if(y == fa) continue;
			for (int k = j; k >= 1; -- k) {
				f[x][j][k] = f[x][j][k] + ans[y];
				for (int l = 1; l <= k - 1; ++ l) 
					if(sgn(f[x][j][k] - (f[x][j][k - l] + f[y][j][l])) == 1)
						f[x][j][k] = f[x][j][k - l] + f[y][j][l];
			}
		}
		if(sgn(ans[x] - f[x][j][j]) == 1)
			ans[x] = f[x][j][j];
	}
}

int main()
{ 
	sum = 0;
	memset(f, 0x7f, sizeof f);
	memset(ans, 0x7f, sizeof ans);
	double maxx = ans[1];
	scanf("%d%d%d", &n, &k1, &k2);
	for (int i = 1; i <= n; ++ i) 
		scanf("%lf", &w[i]), sum += w[i];
	for (int i = 1; i <= n - 1; ++ i) {
		int x, y;
		scanf("%d%d", &x, &y);
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs(1, -1);
	if(ans[1] == maxx) printf("%.2f", sum * 2.0);
	else printf("%.2f", ans[1]);
	return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-05 11:17:27  更:2021-09-05 11:17:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 1:08:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码