题目
Tom is an explorer and now he is in a mysterious cave. He finds that there are lots of rooms in it. You are ensured that these rooms are connected together by some roads and there is only one way between any two rooms. That is, all the rooms are connected as a tree.
Now, Tom wants to travel all the rooms in this mysterious cave. The roads and rooms can be passed more than once. But there are too many rooms here and he doesn?t want to waste too much time and wants to find a walking sequence which can minimum the distance he walks. Note, Tom can select any room in the cave as the start point in his exploration.
Given the distance of roads in the cave, please write a program to calculate the minimum distance Tom has to go in his exploration.
输入格式
Standard input will contain multiple test cases. The first line of the input if a single integer T (1 <= T <= 50) which is the number of test cases.
Each test case starts with an integer N (2 <= N <= 50000), which is the number of the rooms. The following N - 1 lines contains three integers each, u (1 <= u <= N), v (1 <= v <= N) and d (1 <= d <= 2000). u and v is the serial number of the rooms and d is the distance between Room u and Room v .Note that, the serial numbers of the rooms start at 1.
输出格式
Results should be directed to standard output. Start each case with “Case #:” on a single line, where # is the case number starting from 1. Two consecutive cases should be separated by a single blank line. No blank line should be produced after the last test case.
For each test case, print a line containing the minimum distance Tom should go.
样例输入
2
2
1 2 3
3
1 2 3
1 3 4
样例输出
Case 1:
3
Case 2:
7
理解
这个无向图很特殊,有N个节点、N-1条边,很显然符合一棵树的定义。要求以允许走回头路的方式遍历完所有节点,同时要求所走路长最短。
可以从简单的路经开始:一棵单枝干的树。那么只需要从头走到尾就行了;
如果树上的枝干多了侧枝,那么就绕到那个侧枝进行遍历,在绕回主干道、再到尾就行了。
可以类推发现,除了主干道(树的直径)外,其他的节点之间的路径都需要走两次。所以,现在的任务就是找到最长的主干道。在算出所有边(枝)的长度和乘2后,减去主干道(直径)的长度,即可得最短路径。
采用两次BFS的搜索查找树的直径(原理参看上面的链接)
代码
#include<iostream>
#include<vector>
#include <utility> // for make_pair()
#include <queue>
using namespace std;
/**
* 两次BFS找树的直径 https://blog.csdn.net/u011426016/article/details/89164896
* 证明:题设:vw是直径。假设从u开始BFS最远的是x,反证法,推出vw不是直径,矛盾
* u y
* v~~~~~~~w v~~~~~~~~w
* | |
* x u~~~x
*/
/**
* 返回从start开始最远能到达的顶点及路长
* @param graph 无向图
* @param start
* @return <vertex, distance>
*/
pair<int, int> BFS(vector<vector<pair<int, int>>>& graph, int start) {
int n = graph.size();
vector<int> dist(n, 0x7fffffff);
vector<bool> visited(n, false);
queue<int> que;
dist[start] = 0;
visited[start] = true;
que.push(start);
int maxDist = 0, maxIndex;
while (!que.empty()) {
int temp = que.front();
que.pop();
for (const auto& node : graph[temp]) {
if (!visited[node.first] && dist[node.first] > dist[temp] + node.second) {
dist[node.first] = dist[temp] + node.second;
visited[node.first] = true;
que.push(node.first);
if (dist[node.first] > maxDist) {
maxDist = dist[node.first];
maxIndex = node.first;
}
}
}
}
return make_pair(maxIndex, maxDist);
}
int main() {
int T;
cin >> T;
for (int cas = 1; cas <= T; ++cas) {
int N;
cin >> N; // N个节点
vector<vector<pair<int, int>>> adjGraph(N, vector<pair<int, int>>()); // 临接表,保存每个节点邻接的节点及权重
int u, v, d;
int ans = 0;
for (int i = 0; i < N - 1; ++i) { // 总共有N-1条无向边
cin >> u >> v >> d;
adjGraph[u-1].push_back(make_pair(v-1, d));
adjGraph[v-1].push_back(make_pair(u-1, d));
ans += 2 * d;
}
auto vertex = BFS(adjGraph, 0);
// cout << vertex.first << " distance: " << vertex.second << endl;
vertex = BFS(adjGraph, vertex.first);
// cout << vertex.first << " distance: " << vertex.second << endl;
if (cas != 1) {
cout << endl;
}
cout << "Case " << cas << ":" << endl;
cout << ans - vertex.second << endl;
}
return 0;
}