POJ1849
题意:
有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 但是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少?
思路:
1. 假设只有1个机器人遍历树,且要求回到原点, 它最少需要走多少路?
答: 它需要走树总长sum的两倍, 即每条树边它都要走两次才行. 这个结论画个图就明白了, 对于每条边, 机器人要走过该边, 之后还要从该边回去(不回来就不能回到出发点了). 所以自然是sum*2.
2.假设1问中的机器人遍历树,但是不要求它回到原点, 那么它最少需要走多少路?
答: 最少需要走sum-[从出发点能走到最远的点的距离]. 在行走的过程中每个分叉, 它走过去,又走回来即可. 可以反证得出.
3.假设有两个机器人从s出发,遍历整个树且最终回到出发点. 它们行走的最短距离是?
答: 树总长的两倍. 每个机器人都必须回到原点, 那么必然每条边至少要被走两次.
4.假设有两个机器人从s出发,遍历整个树且它们不需要回到出发点. 它们行走的最短距离是?
答: 树总长的两倍-树的直径. 机器人出去不回来,则所走路径中有一条简单路径是可以只走一遍的,派出了两个点去遍历,也就是说有两条简单路径是可以直走一边的,我们要使这两条简单路径的总和尽可能的长,就转换为了树的最长路径问题了.
注意:上面第4种情况, 两个机器人从哪点出发都是没有任何区别的. 因为如果它们出发点不在树的直径上, 那么它们一定可以一起移动到树直径上的某个点上,然后分别朝树直径的两个方向走, 并且遍历它们走的树直径的所有分叉路两次.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 100000+10;
struct Edge
{
int e,v;
int next;
}edge[maxn*2];
int n,s;
int head[maxn],pos;
int dis[maxn];
void init()
{
pos = 0;
memset(head,-1,sizeof(head));
}
void add_edge(int a,int b,int c)
{
edge[pos].e = b;
edge[pos].v = c;
edge[pos].next = head[a];
head[a] = pos++;
edge[pos].e = a;
edge[pos].v = c;
edge[pos].next = head[b];
head[b] = pos++;
}
int bfs(int s)
{
memset(dis,-1,sizeof(dis));
queue<int>q;
q.push(s);
dis[s] = 0;
int Max = 0,id;
while(!q.empty()) {
int u = q.front();
q.pop();
if(dis[u] > Max) Max = dis[id = u];
for(int i = head[u];i != -1; i = edge[i].next) {
int to = edge[i].e;
if(dis[to] == -1 ){
dis[to] = dis[u] + edge[i].v;
q.push(to);
}
}
}
return id;
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&s) != EOF) {
init();
int ans = 0;
for(int i = 1;i < n; i++) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
ans += c;
}
int start = bfs(1);
int ansid = bfs(start);
printf("%d\n",ans*2-dis[ansid]);
}
return 0;
}