Highway |
||
Accepted : 168 | Submit : 576 | |
Time Limit : 4000 MS | Memory Limit : 65536 KB |
HighwayIn ICPCCamp there were n towns conveniently numbered with 1,2,…,n connected with (n−1) roads. The i-th road connecting towns ai and bi has length ci. It is guaranteed that any two cities reach each other using only roads. Bobo would like to build (n−1) highways so that any two towns reach each using only highways. Building a highway between towns x and y costs him δ(x,y) cents, where δ(x,y) is the length of the shortest path between towns x and yusing roads. As Bobo is rich, he would like to find the most expensive way to build the (n−1) highways. InputThe input contains zero or more test cases and is terminated by end-of-file. For each test case: The first line contains an integer n. The i-th of the following (n−1) lines contains three integers ai, bi and ci.
OutputFor each test case, output an integer which denotes the result. Sample Input5 1 2 2 1 3 1 2 4 2 3 5 1 5 1 2 2 1 4 1 3 4 1 4 5 2 Sample Output19 15 SourceXTU OnlineJudge |
原題鏈接:http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1267
題意:n個城鎮之間有n-1條道路相連,現在有個有錢人要來修n-1條高速公路,使得任意兩個城鎮之間都有唯一的高速公路,問你最多要花費多少錢。
對於樣例1:
最遠的兩個點爲4和5
對於1,從1修到4,花費4
對於2,從2修到5,花費4
對於3,從3修到4,花費5
對於4,從4修到5,花費6
對於5,從5修到4,花費6
從4到5和從5到4是一樣的,算一次即可。
最終花費爲4+4+5+6=19
對於此題,要用到樹的直徑,輸的直徑就是一棵樹上最遠的兩個節點。
找到輸的直徑後,樹上的點到直徑上的兩個端點(必爲其中一個)的距離最遠。
求直徑的過程中就可以與處理處每個節點到端點的最短距離。
最後再遍歷每個節點,找到最大的再相加就可以了。
AC代碼1
參考博客:http://blog.csdn.net/WuBaizhe/article/details/72615543
設直徑兩個端點分別是 rt_1 和 rt_2
代碼的思路是先從1節點搜索到距離最遠的直徑上的節點rt_1,然後再從rt_1搜索找到距其最遠的節點rt_2,同時更新每一個點到rt_1的距離,再從rt_2進行搜索更新每一個點到直徑節點的距離,然後每個點到直徑節點的最大距離進行累加。因直徑考慮了兩次,故減去一次直徑就是最後的答案,三次搜索即可。
AC代碼:
/**
* 行有餘力,則來刷題!
* 博客鏈接:http://blog.csdn.net/hurmishine
*
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
typedef long long LL;
struct Node
{
int v,next;
LL w;
}edge[maxn<<1];
LL dis[maxn];
LL DIS;//樹的直徑
int head[maxn];
int cnt;
bool vis[maxn];
int n;
int root1,root2;
void addEdge(int u,int v,LL w)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
//求樹的直徑,
//u-起點,d-距離
void DFS1(int u, LL d)
{
vis[u]=true;
if(d>DIS)
{
DIS=d;
root1=u;
}
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].v;
LL w = edge[i].w;
if(!vis[v])
{
DFS1(v,d+w);
}
}
}
void DFS2(int u,LL d ,bool type)
{
vis[u]=true;
dis[u]=max(dis[u],d);
if(type&&d>DIS)
{
root2=u;
DIS=d;
}
for(int i=head[u];~i;i=edge[i].next)
{
int v = edge[i].v;
LL w = edge[i].w;
if(!vis[v])
{
DFS2(v,d+w,type);
}
}
}
void solve()
{
root1=root2=0;
memset(vis,false,sizeof(vis));
DIS=0;
DFS1(1,0);
memset(dis,0,sizeof(dis));
memset(vis,false,sizeof(vis));
DIS=0;
DFS2(root1,0,true);
memset(vis,false,sizeof(vis));
DFS2(root2,0,false);
LL ans=0;
dis[root1]=0;
//cout<<root1<<","<<root2<<endl;
for(int i=1;i<=n;i++)
{
ans += dis[i];
}
cout<<ans<<endl;
}
int main()
{
//freopen("C:\\Documents and Settings\\Administrator\\桌面\\data.txt","r",stdin);
while(cin>>n)
{
memset(head,-1,sizeof(head));
cnt=0;
int u,v;
LL w;
for(int i=1;i<n;i++)
{
//cin>>u>>v>>w;
scanf("%d%d%I64d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
}
solve();
}
return 0;
}
AC代碼2:
參考博客:http://www.cnblogs.com/fightfordream/p/6860903.html
只用了一個DFS,但是每次搜索的時候都要把根節點傳遞進去,我用了vis來標記,搜索減少了一個參數.
/**
* 行有餘力,則來刷題!
* 博客鏈接:http://blog.csdn.net/hurmishine
*
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e5+5;
typedef long long LL;
struct Edge
{
int v,next;
LL w;
Edge() {}
Edge(int v,int next,LL w):v(v),next(next),w(w) {}
} edge[maxn<<1];
LL dis[2][maxn];
int n;
int cnt;
int head[maxn];
bool vis[maxn];
int p;//記錄直徑端點
LL D;//樹的直徑
void addEdge(int u,int v,LL w)
{
edge[cnt]=Edge(v,head[u],w);
head[u]=cnt++;
}
void DFS(int u,int k)
{
vis[u]=true;
if(dis[k][u] > D)
{
p = u;
D = dis[k][u];
}
for(int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].v;
//注意此處標記的是v而不是i !!!!!
if(!vis[v])
{
vis[v]=true;
dis[k][v] = edge[i].w + dis[k][u];
DFS(v, k);
}
}
}
int main()
{
//freopen("C:\\Documents and Settings\\Administrator\\桌面\\data.txt","r",stdin);
while(cin>>n)
{
memset(head,-1,sizeof(head));
cnt=0;
int u,v;
LL w;
for(int i=1; i<n; i++)
{
scanf("%d%d%I64d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
}
memset(vis,false,sizeof(vis));
memset(dis,0,sizeof(dis));
//從1開始,找樹的直徑的一個端點r1
dis[0][1]=0;
D=0;
DFS(1,0);
int r1=p;//r1爲一個端點
//從r1開始,找樹的直徑的另一個端點r2
//並記錄r1到每個點的最短距離dis[0][]
memset(vis,false,sizeof(vis));
dis[0][r1]=0;
D=0;
DFS(r1,0);
int r2=p;
//求r2到每個點的最短距離dis[1][]
memset(vis,false,sizeof(vis));
dis[1][r2]=0;
DFS(r2,1);
//cout<<r1<<","<<r2<<endl;
//遍歷每個節點,求出到直徑端點的最大值
LL ans=0;
for(int i=1; i<=n; i++)
{
ans+=max(dis[0][i],dis[1][i]);
}
//樹的直徑加了兩次
cout<<ans-D<<endl;
//cout<<ans-dis[1][r1]<<endl;
//cout<<ans-dis[0][r2]<<endl;
}
return 0;
}