2017ccpc全國邀請賽(湖南湘潭) H. Highway XTOJ 1267 【樹的直徑】

Highway

Accepted : 168   Submit : 576
Time Limit : 4000 MS   Memory Limit : 65536 KB

Highway

In ICPCCamp there were n towns conveniently numbered with 1,2,,n connected with (n1) 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 (n1) 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 (n1) highways.

Input

The 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 (n1) lines contains three integers aibi and ci.

  • 1n105
  • 1ai,bin
  • 1ci108
  • The number of test cases does not exceed 10.

Output

For each test case, output an integer which denotes the result.

Sample Input

5
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 Output

19
15

Source

XTU 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;
}



發佈了374 篇原創文章 · 獲贊 211 · 訪問量 70萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章