博客
关于我
E. Sonya and Ice Cream【树的直径+单调队列】
阅读量:528 次
发布时间:2019-03-08

本文共 2519 字,大约阅读时间需要 8 分钟。

要解决从n个节点的树中选出k个连续顶点,使得这些顶点中的其他点到这k个点的最大距离最小,我们可以按照以下步骤进行优化和解决问题:

1. 计算树的直径

使用两次深度优先搜索(DFS)来确定树的直径。第一次DFS从任意一点出发,找到离该点最远的点u,作为直径的一端。第二次DFS从u出发,找到离u最远的点v,作为直径的另一端。

2. 计算各顶点到直径端点的距离

对于每个顶点,计算其到直径端点u和v的距离。这可以通过两次DFS完成:第一次从u出发,记录每个节点的距离;第二次从v出发,记录每个节点的距离。

3. 枚举直径上的连续k个顶点窗口

在直径上进行滑动窗口,求出所有长度为k的连续顶点窗口。每个窗口包括窗口的端点以及内部的k-2个顶点。

4. 使用单调队列计算窗口的最大值

将窗口的两个端点及预处理的直径距离存储在单调队列中,根据队列中的值维护当前窗口的最大距离。每次移动窗口时,更新队列以确保最大距离准确。

5. 表达最优解

根据分析,连续k个顶点窗口的最大距离等于窗口的两个端点到直径端点的距离的最大值。最终,最小最大距离即为直径窗口中各个可能窗口中的最大距离的最小值。

伪代码实现

#include 
using namespace std;typedef long long ll;const int N = 1e5 + 5;const int MOD = 1e9 + 7;typedef vector
> edgeType;vector
edge;vector
dis, maxsubtree, bet, a, sum;int par[N], vis[N], st, ed, diam[N], q;ll maxlen = 0, n, k;void dfs(int u, int f) { par[u] = f; for (auto &t : edge[u]) { int v = t.first, w = t.second; if (v != f && !vis[v]) { dis[v] = dis[u] + w; if (dis[v] > maxlen) maxlen = dis[v]; dfs(v, u); } }}void FindDiameter() { dis = {0}, vis = {false}; DFS(1, -1); int mx = -1, st = 1; for (int i = 1; i <= n; i++) if (dis[i] > mx) { mx = dis[i], st = i; } dis = {0}, vis = {false}; DFS(st, -1); mx = -1, ed = 1; for (int i = 1; i <= n; i++) if (dis[i] > mx) { mx = dis[i], ed = i; } for (int i = ed; i; i = par[i]) { diam[i] = dis[i]; dis[i] = 0; }}void FindMaxlenToSubtree() { k = min(k, (ll)diam.size()); for (int u : diam) vis[u] = 1; for (int u : diam) { maxlen = 0; dis[u] = 0; DFS(u, -1); maxsubtree.push_back(maxlen); dis[u] = 0; }}int main() { cin >> n >> k; for (int i = 1; i < n; i++) { int u, v, w; cin >> u >> v >> w; edge[u].push_back({v, w}); edge[v].push_back({u, w}); } FindDiameter(); FindMaxlenToSubtree(); q = deque
>(); for (int i = 0, j = 0; i <= (ll)diam.size() - k; i++) { while (j <= i + k - 1) { while (!q.empty() && maxsubtree[j] > q.back().first) q.pop_back(); q.push_back({maxsubtree[j], j}); j++; } while (q.front().second < i) q.pop_front(); ll current_max = q.front().first; if (current_max < ans) ans = current_max; if (i + k < diam.size()) { while (!q.empty() && maxsubtree[i + k] > q.back().first) q.pop_back(); q.push_back({maxsubtree[i + k], i + k}); } } cout << ans;}

结论

通过上述步骤,我们能够高效地计算出k个连续顶点,使得其他顶点到这k个点的最大距离最小化。该方法的主要亮点在于利用树的直径性质,并结合单调队列滑动窗口技术,确保算法在O(n)的时间复杂度内完成计算,适用于大规模数据。

转载地址:http://kfkiz.baihongyu.com/

你可能感兴趣的文章
NIFI大数据进阶_Kafka使用相关说明_实际操作Kafka生产者---大数据之Nifi工作笔记0036
查看>>
NIFI大数据进阶_NIFI的模板和组的使用-介绍和实际操作_创建组_嵌套组_模板创建下载_导入---大数据之Nifi工作笔记0022
查看>>
NIFI大数据进阶_NIFI监控功能实际操作_Summary查看系统和处理器运行情况_viewDataProvenance查看_---大数据之Nifi工作笔记0026
查看>>
NIFI大数据进阶_NIFI监控的强大功能介绍_处理器面板_进程组面板_summary监控_data_provenance事件源---大数据之Nifi工作笔记0025
查看>>
NIFI大数据进阶_NIFI集群知识点_认识NIFI集群以及集群的组成部分---大数据之Nifi工作笔记0014
查看>>
NIFI大数据进阶_NIFI集群知识点_集群的断开_重连_退役_卸载_总结---大数据之Nifi工作笔记0018
查看>>
NIFI大数据进阶_使用NIFI表达式语言_来获取自定义属性中的数据_NIFI表达式使用体验---大数据之Nifi工作笔记0024
查看>>
NIFI大数据进阶_内嵌ZK模式集群1_搭建过程说明---大数据之Nifi工作笔记0015
查看>>
NIFI大数据进阶_内嵌ZK模式集群2_实际操作搭建NIFI内嵌模式集群---大数据之Nifi工作笔记0016
查看>>
NIFI大数据进阶_外部ZK模式集群1_实际操作搭建NIFI外部ZK模式集群---大数据之Nifi工作笔记0017
查看>>
NIFI大数据进阶_实时同步MySql的数据到Hive中去_可增量同步_实时监控MySql数据库变化_实际操作_03---大数据之Nifi工作笔记0035
查看>>
NIFI大数据进阶_实时同步MySql的数据到Hive中去_可增量同步_实时监控MySql数据库变化_操作方法说明_01---大数据之Nifi工作笔记0033
查看>>
NIFI大数据进阶_实时同步MySql的数据到Hive中去_可增量同步_实时监控MySql数据库变化_操作方法说明_02---大数据之Nifi工作笔记0034
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_01_实际操作---大数据之Nifi工作笔记0029
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_02_实际操作_splitjson处理器_puthdfs处理器_querydatabasetable处理器---大数据之Nifi工作笔记0030
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_说明操作步骤---大数据之Nifi工作笔记0028
查看>>
NIFI大数据进阶_连接与关系_设置数据流负载均衡_设置背压_设置展现弯曲_介绍以及实际操作---大数据之Nifi工作笔记0027
查看>>
NIFI数据库同步_多表_特定表同时同步_实际操作_MySqlToMysql_可推广到其他数据库_Postgresql_Hbase_SqlServer等----大数据之Nifi工作笔记0053
查看>>
NIFI汉化_替换logo_二次开发_Idea编译NIFI最新源码_详细过程记录_全解析_Maven编译NIFI避坑指南001---大数据之Nifi工作笔记0068
查看>>
NIFI汉化_替换logo_二次开发_Idea编译NIFI最新源码_详细过程记录_全解析_Maven编译NIFI避坑指南002---大数据之Nifi工作笔记0069
查看>>