1. 程式人生 > >2007年NOIP提高組 樹網的核

2007年NOIP提高組 樹網的核

題目描述 Description

設 T=(V, E, W) 是一個無圈且連通的無向圖(也稱為無根樹),每條邊帶有正整數的權,我們稱T 為樹網(treenetwork),其中V, E分別表示結點與邊的集合,W 表示各邊長度的集合,並設T 有n個結點。
路徑:樹網中任何兩結點a,b 都存在唯一的一條簡單路徑,用d(a,b)表示以a,b 為端點的路徑的長度,它是該路徑上各邊長度之和。我們稱d(a,b)為a,b 兩結點間的距離。一點v到一條路徑P的距離為該點與P 上的最近的結點的距離:
d(v,P)=min{d(v,u),u 為路徑P 上的結點}。
樹網的直徑:樹網中最長的路徑稱為樹網的直徑。對於給定的樹網T,直徑不一定是唯一的,但可以證明:各直徑的中點(不一定恰好是某個結點,可能在某條邊的內部)是唯一的,我們稱該點為樹網的中心。
偏心距 ECC(F):樹網T 中距路徑F 最遠的結點到路徑F 的距離,即
ECC(F ) = max{d(v, F ), v}。
任務:對於給定的樹網T=(V, E,W)和非負整數s,求一個路徑F,它是某直徑上的一段路徑(該路徑兩端均為樹網中的結點),其長度不超過s(可以等於s),使偏心距ECC(F)最小。我們稱這個路徑為樹網T=(V,E,W)的核(Core)。必要時,F 可以退化為某個結點。一般來說,在上
述定義下,核不一定只有一個,但最小偏心距是唯一的。

輸入描述 Input Description

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

輸出描述 Output Description

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

樣例輸入 Sample Input
【輸入樣例1】
5 2
1 2 5
2 3 2
2 4 4
2 5 3
【輸入樣例2】
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3

樣例輸出 Sample Output

【輸出樣例1】
5
【輸出樣例1】
5

資料範圍及提示 Data Size & Hint
【限制】

40%的資料滿足:5<=n<=15
70%的資料滿足:5<=n<=80
100%的資料滿足:5<=n<=300, 0<=s<=1000。邊長度為不超過1000 的正整數。

只會 n <= 300 的打法qwq,資料加強版就跪了QAQ,先floyd處理出各點間的最短路,然後列舉樹網的核的兩個端點i,j(兩個端點可以相等,且dist[i][j] <= s)和除兩個端點外的其他點來找核(i–j)的最遠節點,節點到核的距離為(dist[i][k] + dist[j][k] - dist[i][j])/2,每次列舉結果與答案取min即可。

#include<iostream>
#include<cstdio>
using namespace std;
int dist[450][450];
int n,s;
int main()
{
    scanf("%d%d",&n,&s);
    for(int i = 1 ; i <= n ; i ++)
        for(int j = i + 1 ; j <= n ; j ++)
            dist[i][j] = dist[j][i] = 214748364;
    for(int i = 1 ; i < n ; i ++)
    {
        int f,t,d;
        scanf("%d%d%d",&f,&t,&d);
        dist[f][t] = d;
        dist[t][f] = d;
    }
    for(int k = 1 ; k <= n ; k ++)
        for(int i = 1 ; i <= n ; i ++)
        {
            if(i != k)
            {
                for(int j = 1 ; j <= n ; j ++)
                if(dist[i][k] + dist[k][j] < dist[i][j])
                    dist[i][j] = dist[i][k] + dist[k][j];
            }
        }
    int ans = 214748364;
    for(int i = 1 ; i <= n ; i ++)
    {
        for(int j = i ; j <= n ; j ++)
        {
            if(dist[i][j] <= s)
            {
                int mx = 0;
                for(int k = 1 ; k <= n ; k ++)
                {
                    if(i != k && j != k)
                    {
                        if(mx < dist[i][k] + dist[j][k] - dist[i][j])
                            mx = dist[i][k] + dist[j][k] - dist[i][j];
                    }
                }
                if(mx <= ans)
                    ans = mx;
            }
        }
    }
    cout<<ans/2;
    return 0;
}