1. 程式人生 > 實用技巧 >樹網的核-Floyd+寬搜

樹網的核-Floyd+寬搜

題目:

VIJOS-P1362 樹網的核

Time Limit: 1 SecMemory Limit: 128 MB

Description

設T=(V, E, W) 是一個無圈且連通的無向圖(也稱為無根樹),每條邊到有正整數的權,我們稱T為樹網(treebetwork),其中V,E分別表示結點與邊的集合,W表示各邊長度的集合,並設T有n個結點。 路徑:樹網中任何兩結點a,b都存在唯一的一條簡單路徑,用d(a, b)表示以a, b為端點的路徑的長度,它是該路徑上各邊長度之和。我們稱d(a, b)為a, b兩結點間的距離。 D(v, P)=min{d(v, u), u為路徑P上的結點}。 樹網的直徑:樹網中最長的路徑成為樹網的直徑。對於給定的樹網T,直徑不一定是唯一的,但可以證明:各直徑的中點(不一定恰好是某個結點,可能在某條邊的內部)是唯一的,我們稱該點為樹網的中心。 偏心距ECC(F):樹網T中距路徑F最遠的結點到路徑F的距離,即 ECC(F)=max{d(v, F),v∈V} 任務:對於給定的樹網T=(V, E, W)和非負整數s,求一個路徑F,他是某直徑上的一段路徑(該路徑兩端均為樹網中的結點),其長度不超過s(可以等於s),使偏心距ECC(F)最小。我們稱這個路徑為樹網T=(V, E, W)的核(Core)。必要時,F可以退化為某個結點。一般來說,在上述定義下,核不一定只有一個,但最小偏心距是唯一的。 下面的圖給出了樹網的一個例項。圖中,A-B與A-C是兩條直徑,長度均為20。點W是樹網的中心,EF邊的長度為5。如果指定s=11,則樹網的核為路徑DEFG(也可以取為路徑DEF),偏心距為8。如果指定s=0(或s=1、s=2),則樹網的核為結點F,偏心距為12。

Input

包含n行: 第1行,兩個正整數n和s,中間用一個空格隔開。其中n為樹網結點的個數,s為樹網的核的長度的上界。設結點編號以此為1,2,……,n。 從第2行到第n行,每行給出3個用空格隔開的正整數,依次表示每一條邊的兩個端點編號和長度。例如,“2 4 7”表示連線結點2與4的邊的長度為7。 所給的資料都是正確的,不必檢驗。

Output

只有一個非負整數,為指定意義下的最小偏心距。

Sample Input

5 2
1 2 5
2 3 2
2 4 4
2 5 3

Sample Output

5 分析:    結論:選取任意直徑答案都相同
    證:設其交點為N,則兩側的每條直徑權值和相等        所以僅處理一條直徑即可      N^3做法:Floyd+寬搜      1.建圖,並Floyd      2.找出直徑,並記錄直徑上的點      3.列舉直徑上的起點和終點,並求出偏心距  偏心距求法:     偏心距可能出現在三個位置:1.直徑起點到列舉起點的距離                 2.直徑終點到列舉終點的距離                  3.列舉的路段中其他的分支         前兩個都已用Floyd演算法得出,第三個分別在每個點上進行寬搜(注意,不能搜到直徑上的點) 注意:1.寬搜的初始化 2.拷貝f陣列避免將其汙染
    程式碼如下
#include<stdio.h>
#include<queue>
#include<cstring>
using namespace std;
queue<int>q;
#define N 301
#define inf 10000001
int f[N][N];
int f1[N][N];
int dis[N];
int qu[N];
int node[N][N];
int end[N];
int point[N];
int vis[N];
int cnt=1;    int n,s;
int max(int x,int y)
{
    return x>y?x:y;
}
int min(int x,int y)
{
    return x<y?x:y;
}
int bfs(int x,int cnt)
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=cnt;i++)
    vis[point[i]]=1;
    q.push(x);
    vis[x]=1;
    int re=0;
    while(!q.empty())
    {
        int imp=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0&&f1[i][imp]!=inf)
            {
                dis[i]=dis[imp]+f1[i][imp];
                re=max(dis[i],re);
                q.push(i);
                vis[i]=1;
            }    
        }    
    }
    return re;    
}
int main()
{

    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        if(i!=j)
        f1[i][j]=f[i][j]=inf;
    }
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        f1[x][y]=f[x][y]=z;
        f1[y][x]=f[y][x]=z;
    }    
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);                
            }
        }
    }
    int d=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            d=max(d,f[i][j]);    
        }
    }        

    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            if(f[i][j]==d)
                    end[1]=i,end[2]=j;            
        }
    }
    int ans=inf;    
    for(int k=1;k<=n;k++)
    {
        if(f[end[1]][end[2]]==f[end[1]][k]+f[k][end[2]]&&k!=end[1]&&k!=end[2])
        point[++cnt]=k;    
    }
    point[1]=end[1];
    point[++cnt]=end[2];
    for(int i=1;i<=n;i++)
    dis[i]=bfs(i,cnt);
    for(int i=1;i<=cnt;i++)
    {
    int p=dis[point[i]];
        for(int j=i;j<=cnt;j++)
        {
            if(f[point[i]][point[j]]>s)
            continue;
        if(p<dis[point[j]])
            p=dis[point[j]];
            int sr=max(f[point[i]][end[1]],f[point[j]][end[2]]);
            sr=max(sr,p);
            if(sr==0)
            continue;
            ans=min(sr,ans);
//            printf("%d p:%d %d\n",ans,point[i],point[j]);
        }        
    }
    printf("%d",ans);
}