POJ 1849 Two(遍歷樹)
POJ 1849 Two(遍歷樹)
http://poj.org/problem?id=1849
題意:
有一顆n個結點的帶權的無向樹, 在s結點放兩個機器人, 這兩個機器人會把樹的每條邊都走一遍, 可是最後機器人不要求回到出發點. 問你兩個機器人走的路總長之和的最小值是多少?
分析:
首先本題僅僅要求出樹的直徑, 然後用樹的總長sum*2-樹的直徑就是所求結果. 以下一步步來說明為什麽是這種.
1.如果僅僅有1個機器人遍歷樹,且要求回到原點, 它最少須要走多少路?
答: 它須要走樹總長sum的兩倍, 即每條樹邊它都要走兩次才行. 這個結論畫個圖就明確了, 對於每條邊, 機器人要走過該邊, 之後還要從該邊回去(不回來就不能回到出發點了
2.如果1問中的機器人遍歷樹,可是不要求它回到原點, 那麽它最少須要走多少路?
答: 最少須要走sum-[從出發點能走到最遠的點的距離]. 在行走的過程中每一個分叉, 它走過去,又走回來就可以. 能夠反證得出.
3.如果有兩個機器人從s出發,遍歷整個樹且終於回到出發點. 它們行走的最短距離是?
答: 樹總長的兩倍. 每一個機器人都必須回到原點, 那麽必定每條邊至少要被走兩次.
4.如果有兩個機器人從s出發,遍歷整個樹且它們不須要回到出發點. 它們行走的最短距離是?
答: 樹總長的兩倍-樹的直徑. 機器人出去不回來,則所走路徑中有一條簡單路徑是能夠僅僅走一遍的,派出了兩個點去遍歷,也就是說有兩條簡單路徑是能夠直走一邊的,我們要使這兩條簡單路徑的總和盡可能的長,就轉換為了樹的最長路徑問題了.
註意:上面第4種情況, 兩個機器人從哪點出發都是沒有不論什麽差別的. 由於假設它們出發點不在樹的直徑上, 那麽它們一定能夠一起移動到樹直徑上的某個點上,然後分別朝樹直徑的兩個方向走, 而且遍歷它們走的樹直徑的全部分叉路兩次.
AC代碼:
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn=100000+5; //邊結構 struct Edge { Edge(){} Edge(int to,int cost,int next):to(to),cost(cost),next(next){} int to; int cost; int next; }edges[maxn]; int cnt=0;//總邊數 int head[maxn]; //加入兩條有向邊 void AddEdge(int u,int v,int cost) { edges[cnt]=Edge(v,cost,head[u]); head[u]=cnt++; edges[cnt]=Edge(u,cost,head[v]); head[v]=cnt++; } int dist[maxn]; //返回從s能到達的最長點編號 int BFS(int s) { int max_dist=0; int id=s; queue<int> Q; memset(dist,-1,sizeof(dist)); dist[s]=0; Q.push(s); while(!Q.empty()) { int u=Q.front(); Q.pop(); if(dist[u]>max_dist) max_dist=dist[id=u]; for(int i=head[u]; i!=-1; i=edges[i].next) { Edge &e=edges[i]; if(dist[e.to]==-1) { dist[e.to]=dist[u]+e.cost; Q.push(e.to); } } } return id; } int main() { int n,s; while(scanf("%d%d",&n,&s)==2) { int sum=0;//樹的總長 memset(head,-1,sizeof(head)); cnt=0; for(int i=1;i<=n-1;i++) { int u,v,cost; scanf("%d%d%d",&u,&v,&cost); sum+=cost; AddEdge(u,v,cost); } printf("%d\n",sum*2-dist[BFS(BFS(s))]); } return 0; }
POJ 1849 Two(遍歷樹)