当前位置: 首页 > news >正文

算法竞赛进阶指南.巡逻

题目

350. 巡逻
在这里插入图片描述

算法标签: 图论, 树的直径, 树形 D P DP DP

思路

题目大意就是添加 1 / 2 1 / 2 1/2条边 使得从起点开始走访问所有的点, 使得总的路径长度最小, 因为原图是树, 因此最初的巡回距离是 2 ⋅ ( n − 1 ) 2 \cdot (n - 1) 2(n1), 将问题分为两部分, 首先是添加一条边
在这里插入图片描述

最优策略就是在树的直径的两端添加一条边, 如上图, 树的直径是粉色线段设为 d d d, 如果当前巡逻到最下面的点如果不添加边需要 d d d的长度, 如果添加一条边, 路径长度变为 1 1 1, 减少的距离就是 d − 1 d - 1 d1

如果可以建造两条道路, 可以找到树的最长路径和次长路径, 然后分别在两端设置路径
那么如何计算树的次长路径呢?
可以在计算完最长路径后, 将路径上的边权标志为 − 1 -1 1, 再进行统计直径, 因为边权是负数不会产生贡献, 那么次长路径也计算出来了

为什么第二次求树的直径不能两次 d f s dfs dfs直接求?
因为边权变为负数, 直接求是错误的, 只能使用树形 D P DP DP求解

因为每个点被访问一次, 算法时间复杂度 O ( n ) O(n) O(n)

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>using namespace std;const int N = 1e6 + 10, M = N << 1;int n, k;
int head[N], ed[M], ne[M], w[M], idx;
int dist[N], len1, len2;
int from[N];
bool vis[N];void add(int u, int v, int val) {ed[idx] = v, ne[idx] = head[u], w[idx] = val, head[u] = idx++;
}void dfs1(int u, int fa) {for (int i = head[u]; ~i; i = ne[i]) {int v = ed[i];if (v == fa) continue;dist[v] = dist[u] + w[i];dfs1(v, u);}
}void calc() {// 第一次DFS找最远点memset(dist, 0, sizeof dist);dfs1(1, -1);int max_u = 1;for (int i = 1; i <= n; ++i) {if (dist[i] > dist[max_u]) max_u = i;}// 第二次DFS找直径memset(dist, 0, sizeof dist);dfs1(max_u, -1);int max_v = max_u;for (int i = 1; i <= n; ++i) {if (dist[i] > dist[max_v]) max_v = i;}len1 = dist[max_v];if (k == 1) return;// 记录直径路径memset(vis, 0, sizeof vis);queue<int> q;q.push(max_v);vis[max_v] = true;from[max_v] = -1;while (!q.empty()) {int u = q.front();q.pop();for (int i = head[u]; ~i; i = ne[i]) {int v = ed[i];if (vis[v] || dist[v] != dist[u] - w[i]) continue;vis[v] = true;from[v] = u;q.push(v);}}// 将直径上的边权设为-1for (int u = max_u; u != -1; u = from[u]) {for (int i = head[u]; ~i; i = ne[i]) {if (ed[i] == from[u]) {w[i] = -1;w[i ^ 1] = -1;break;}}}
}void dp(int u, int fa) {for (int i = head[u]; ~i; i = ne[i]) {int v = ed[i];if (v == fa) continue;dp(v, u);len2 = max(len2, dist[u] + dist[v] + w[i]);dist[u] = max(dist[u], dist[v] + w[i]);}
}int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);memset(head, -1, sizeof head);cin >> n >> k;for (int i = 0; i < n - 1; ++i) {int u, v;cin >> u >> v;add(u, v, 1);add(v, u, 1);}calc();if (k == 1) {cout << 2 * (n - 1) - (len1 - 1) << endl;return 0;}memset(dist, 0, sizeof dist);dp(1, -1);cout << 2 * (n - 1) - (len1 - 1) - (len2 - 1) << endl;return 0;
}

相关文章:

  • 13分区排烟 无法远程启动 12-1-4,排烟管道出口未连接室外
  • Linux-07-Shell
  • python常用科学计算库及使用示例
  • 数字智慧方案6185丨智慧银行解决方案(51页PPT)(文末有下载方式)
  • 【免费】2010-2019年上市公司排污费数据
  • 迪米特法则(LoD)
  • Baklib内容中台落地实战指南
  • 传奇各职业/战士/法师/道士/手套/手镯/护腕/神秘腰带爆率及出处产出地/圣战/法神/天尊/祈祷/虹魔/魔血
  • 用c 编写的笔记搜索程序
  • 如何用Python绘制两个圆之间的8条公切线
  • 第J6周:ResNeXt-50实战
  • C++ 动态内存管理详讲
  • 数字智慧方案5877丨智慧交通项目方案(122页PPT)(文末有下载方式)
  • SX24C01.UG-PXI程控电阻桥板卡
  • 英一真题阅读单词笔记 20-21年
  • 藏文词云生成器学习实践
  • 2000-2020年全国各地级市资本存量测算数据(以2000年为基期)(含原始数据+计算过程+结果)
  • 数字智慧方案5875丨智慧交通枢纽综合解决方案(43页PPT)(文末有下载方式)
  • ValueError: expected sequence of length 8 at dim 2 (got 0)
  • 历史数据分析——运输服务
  • “五一”前两日湖北20多家景区实施限流
  • 上海环球马术冠军赛开赛,一场体育与假日消费联动的狂欢
  • 美国证实加拿大及墨西哥汽车零部件免关税
  • 中央网信办部署开展“清朗·整治AI技术滥用”专项行动
  • 顺利撤离空间站,神十九乘组踏上回家之旅
  • 杨国荣︱学术上的立此存照——《故旧往事,欲说还休》读后