题意

一个城市由节点和连接节点的街道组成,街道是双向的。

此刻大雪覆盖了这个城市,市长确定了一些街道要将它们清扫干净,这些街道保证所有的节点可以通过它们连通而且街道数目尽可能小。

现有两台相同的扫雪机\(S\)和\(M\),它们的起点在同一个节点上。

所有被确定的街道必须至少被一台扫雪机经过,才能完成清扫任务,完成任务后\(S\)和\(M\)可以在原地停下,不必集合到某一点。

扫雪机的行进是需要耗费油量的(即使扫雪机行驶的是已被扫净的街道),因此扫雪机行进的总距离越小越好,你需要计算两台扫雪机完成任务的最小总行进距离。

分析

题目的条件暗示了这是一棵树,问题来了,题意的最短距离(两个扫雪机的情况下)意味着什么?

我们先考虑一个扫雪机的情况。这样,从任意一个地方开始,然后转化为以其为根的有根树,遍历它的所有子树,完事了。最后一个“最长”的子树不用回来,这样在最优的情况下走多久呢?2*所有边长-全图的最长边——也就是图的直径,这是它的定义。

那么两个机子意味着什么呢?很意外,其实还是这个答案,哈哈。为什么?因为既然转化为以直径的端点为根(见上最优的情况)的有根树了,那么这种情况下根结点必只有一个子节点。那么无论怎么走,他们仅仅只能省去走直径的“回程”,在子树里他们是无论如何要走一遍回来的,这样,与一个扫雪机的情况是一致的。

参见:https://www.cnblogs.com/dilthey/p/7231438.html

接下来解决树的直径问题——树的最长边。

做法是这样的:

从随便一个点(记为\(w\))出发,到一个节点结束,记这个节点为\(u\);然后从这个点出发,到另一个点完事,记这个节点为\(v\),那么长度就是u到v的长度。

参见:https://blog.csdn.net/tc_to_top/article/details/47002255

这样为什么能work呢?

  1. 如果\(w\)是直径上的一点,那么u一定是直径的一个端点,否则从另一端点出发到达\(u\)反而比直径长了,不合题意。
  2. 如果\(w\)不是的话,那它到最远的点必经过一直径上的点(记为\(c\)),后同(1)的情况。为什么?如果不经过直径的一点,此时有\(dis[w,u']=dis[w,x]+dis[x,u']>dis[w,t]+dis[t,u]\),其中\(t\)是直径上的一点,\(x\)是\(w\)到\(t\)的一点。那么\(dis[x,u']>dis[x,t]+dis[t,u]\),也就有\(dis[v,t]+dis[t,x]+dis[x,u']=dis[v,u']>dis[v,u]=dis[v,t]+dis[t,u]\),这就坏事了:我们明明有\((v,u)\)是树的直径的。所以得到矛盾,原先的证明不成立。

参见:https://blog.csdn.net/qq_32400847/article/details/51469917

代码

  1. /*
  2. * Filename: poj1849.cpp
  3. * Date: 2018-11-03
  4. */
  5. #include <cstring>
  6. #include <vector>
  7. #include <iostream>
  8. #define INF 0x3f3f3f3f
  9. #define PB push_back
  10. #define MP make_pair
  11. #define fi first
  12. #define se second
  13. #define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
  14. #define per(i,a,b) for(repType i=(a); i>=(b); --i)
  15. #define ZERO(x) memset(x, 0, sizeof(x))
  16. #define MS(x,y) memset(x, y, sizeof(x))
  17. #define ALL(x) (x).begin(), (x).end()
  18. #define QUICKIO \
  19. ios::sync_with_stdio(false); \
  20. cin.tie(0); \
  21. cout.tie(0);
  22. #define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
  23. using namespace std;
  24. typedef int repType;
  25. typedef long long ll;
  26. int n,s;
  27. const int MAXN=100005;
  28. struct Edge
  29. {
  30. int u,v,w;
  31. Edge(int _u, int _v, int _w):
  32. u(_u), v(_v), w(_w) {}
  33. };
  34. vector<Edge> edges;
  35. vector<int> G[MAXN];
  36. void add_edge(int u, int v, int w)
  37. {
  38. edges.PB(Edge(u,v,w));
  39. G[u].PB(int(edges.size())-1);
  40. }
  41. int dist[MAXN];
  42. void dfs(int x)
  43. {
  44. rep(i,0,int(G[x].size())-1)
  45. {
  46. if(dist[edges[G[x][i]].v]==-1)
  47. {
  48. dist[edges[G[x][i]].v]=dist[x]+edges[G[x][i]].w;
  49. dfs(edges[G[x][i]].v);
  50. }
  51. }
  52. }
  53. int
  54. main()
  55. {
  56. QUICKIO
  57. while(cin>>n>>s)
  58. {
  59. edges.clear();
  60. rep(i,1,n) G[i].clear();
  61. ll ans=0;
  62. rep(i,1,n-1)
  63. {
  64. int a,b,c;
  65. cin>>a>>b>>c;
  66. add_edge(a,b,c);
  67. add_edge(b,a,c);
  68. ans+=c;
  69. }
  70. MS(dist, -1);
  71. dist[s]=0;
  72. dfs(s);
  73. int maxi=s;
  74. rep(i,1,n) if(dist[maxi]<dist[i]) maxi=i;
  75. MS(dist, -1);
  76. dist[maxi]=0;
  77. dfs(maxi);
  78. rep(i,1,n)
  79. {
  80. if(dist[maxi]<dist[i]) maxi=i;
  81. }
  82. cout<<2*ans-dist[maxi]<<endl;
  83. }
  84. return 0;
  85. }

「日常训练&知识学习」树的直径(POJ-1849,Two)的更多相关文章

  1. 「日常训练&知识学习」树的分块(王室联邦,HYSBZ-1086)

    题意与分析 这题的题意就是树分块,更具体的看题目(中文题). 学习这一题是为了树的分块,为树上莫队做铺垫. 参考1:https://blog.csdn.net/LJH_KOQI/article/det ...

  2. 「日常训练&知识学习」单调栈

    这几天的知识学习比较多,因为时间不够了.加油吧,为了梦想. 这里写几条简单的单调栈作为题解记录,因为单调栈的用法很简单,可是想到并转化成用这个需要一些题目的积淀. 相关博客参见:https://blo ...

  3. 「日常训练&知识学习」莫队算法(二):树上莫队(Count on a tree II,SPOJ COT2)

    题意与分析 题意是这样的,给定一颗节点有权值的树,然后给若干个询问,每次询问让你找出一条链上有多少个不同权值. 写这题之前要参看我的三个blog:Codeforces Round #326 Div. ...

  4. 「国庆训练&知识学习」图的最大独立集与拓展(Land of Farms,HDU-5556)

    题意 一个\(N*M\)的矩阵,其中"."代表空地,"0-9"代表古代建筑,我们如果选择了一个编号的古代建筑想要建立,那么对应就要将全部该编号的建筑建立起来,如 ...

  5. 「日常训练」Caterpillar(POJ-3310)

    题意与分析 一条很有趣的题目.给一个无向图,问它是否无环,且可以在上面找到一条线,使所有的顶点要么在线上要么不在线上但在与线相连的边上. 那么首先要确定所有点联系在一起.这个可以同判环一起处理:如果建 ...

  6. 「日常训练」ZgukistringZ(Codeforces Round #307 Div. 2 B)

    题意与分析(CodeForces 551B) 这他妈哪里是日常训练,这是日常弟中弟. 题意是这样的,给出一个字符串A,再给出两个字符串B,C,求A中任意量字符交换后(不限制次数)能够得到的使B,C作为 ...

  7. 「日常训练」湫湫系列故事——设计风景线(HDU-4514)

    题意与分析 中文题目,木得题意的讲解谢谢. 然后还是分解成两个任务:a)判环,b)找最长边. 对于这样一个无向图,强行转换成负权然后bellman-ford算法求最短是难以实现的,所以感谢没有环--我 ...

  8. 树的直径 poj 2631

    树的直径:从随意一点出发,BFS找到最远的距离,然后在从该点出发BFS找到最远的距离 #include <iostream> #include <algorithm> #inc ...

  9. 「日常训练」Duff in the Army (Codeforces Round #326 Div.2 E)

    题意(CodeForces 588E) 给定一棵\(n\)个点的树,给定\(m\)个人(\(m\le n\))在哪个点上的信息,每个点可以有任意个人:然后给\(q\)个询问,每次问\(u\)到\(v\ ...

随机推荐

  1. Axure RP 8 学习记录

    一.Axure RP 简介 Axure是一个专业的快速原型设计工具.能够快速创建应用软件或Web网站的线框图.流程图.原型和规格说明文档. 二.Axure RP下载 1.下载安装包 https://w ...

  2. CVPR 2016 paper reading (3)

    DeepFashion: Powering Robust Clothes Recognition and Retrieval with Rich Annotations, Ziwei Liu, Pin ...

  3. ListView 中嵌套 GridView

    1.主布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...

  4. GIT 报错:Result too large 解决办法

    在使用bower install命令下载前端依赖的js插件时,git出错了,报错信息如下: bower ECMDERR Failed to execute "git ls-remote -- ...

  5. render 函数渲染表格的当前数据列使用

    columns7: [ { title: '编号', align: 'center', width: 90, key: 'No', render: (h, params) => { return ...

  6. Oracle 体系结构二 内存结构

    Oracle实例由共享内存块(SGA)以及大量的后台进程构成. SGA必须包含的数据结构: 数据库缓冲区缓存 日志缓冲区 共享池 可选的数据结构: 大池 JAVA池 流池 其他缓冲区缓存池 用户会话还 ...

  7. RMAN_PIPE

    涉及的dbms_pipe包中的过程和函数:(1)PACK_MESSAGE Procedures用途:Builds message in local buffer(2)SEND_MESSAGE Func ...

  8. MySQL数据库修改数据表类型(引擎)的方法

    MySQL数据库使用事务,相关数据表必须为InnoDB引擎 查看数据表状态: SHOW TABLE STATUS FROM wawa WHERE NAME='ww_invite_code_temp'; ...

  9. C# Oracle批量插入数据进度条制作

    前言 由于项目需求,需要将Excel中的数据进过一定转换导入仅Oracle数据库中.考虑到当Excel数据量较大时,循环Insert语句效率太低,故采用批量插入的方法.在插入操作运行时,会造成系统短暂 ...

  10. centOS上的基础文件操作

    文件及文件夹的基础操作: 1: 新建     (1)在当前目录新建一个文件夹   dir foldername     (2)  新建文件:     当前目录  vi newFile.text     ...