1. 程式人生 > >洛谷 P1099 樹網的核

洛谷 P1099 樹網的核

想象 click opened using sticky str pac html ace

P1099 樹網的核

題目描述

T=(V,E,W)T=(V,E,W)是一個無圈且連通的無向圖(也稱為無根樹),每條邊到有正整數的權,我們稱TT為樹網(treebetwork),其中VV,EE分別表示結點與邊的集合,WW表示各邊長度的集合,並設TT有nn個結點。

路徑:樹網中任何兩結點aa,bb都存在唯一的一條簡單路徑,用d(a, b)d(a,b)表示以a, ba,b為端點的路徑的長度,它是該路徑上各邊長度之和。我們稱d(a, b)d(a,b)為a, ba,b兩結點間的距離。

D(v, P)=\min\{d(v, u)\}D(v,P)=min{d(v,u)}, uu為路徑PP上的結點。

樹網的直徑:樹網中最長的路徑成為樹網的直徑。對於給定的樹網TT,直徑不一定是唯一的,但可以證明:各直徑的中點(不一定恰好是某個結點,可能在某條邊的內部)是唯一的,我們稱該點為樹網的中心。

偏心距\mathrm{ECC}(F)ECC(F):樹網T中距路徑F最遠的結點到路徑FF的距離,即

\mathrm{ECC}(F)=\max\{d(v, F),v \in V\}ECC(F)=max{d(v,F),vV}

任務:對於給定的樹網T=(V, E, W)T=(V,E,W)和非負整數ss,求一個路徑FF,他是某直徑上的一段路徑(該路徑兩端均為樹網中的結點),其長度不超過ss(可以等於s),使偏心距ECC(F)ECC(F)最小。我們稱這個路徑為樹網T=(V, E, W)T=(V,E,W)的核(Core

)。必要時,FF可以退化為某個結點。一般來說,在上述定義下,核不一定只有一個,但最小偏心距是唯一的。

下面的圖給出了樹網的一個實例。圖中,A-BAB與A-CAC是兩條直徑,長度均為2020。點WW是樹網的中心,EFEF邊的長度為55。如果指定s=11s=11,則樹網的核為路徑DEFG(也可以取為路徑DEF),偏心距為88。如果指定s=0s=0(或s=1s=1、s=2s=2),則樹網的核為結點FF,偏心距為1212。

技術分享圖片

輸入輸出格式

輸入格式:

nn行。

11行,兩個正整數nn和ss,中間用一個空格隔開。其中nn為樹網結點的個數,ss為樹網的核的長度的上界。設結點編號以此為1,2,…,n1,2,,n。

從第22行到第nn行,每行給出33個用空格隔開的正整數,依次表示每一條邊的兩個端點編號和長度。例如,“2 4 7247”表示連接結點22與44的邊的長度為77。

輸出格式:

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

輸入輸出樣例

輸入樣例#1: 復制
5 2
1 2 5
2 3 2
2 4 4
2 5 3

輸出樣例#1: 復制
5
輸入樣例#2: 復制
8 6
1 3 2
2 3 2 
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
輸出樣例#2: 復制
5

說明

40\%40%的數據滿足:5 \le n \le 155n15
70\%70%的數據滿足:5 \le n \le 805n80
100\%100%的數據滿足:5 \le n \le 300,0 \le s \le 10005n300,0s1000。邊長度為不超過10001000的正整數

NOIP 2007 提高第四題

公式:一個點到a,b之間路徑的距離為 (dis[i][a]+dis[i][b]-dis[a][b])/2

技術分享圖片
/*
可以想象出,這個樹網的核一定在這棵樹的直徑上(不一定對) 
因為n很小,可以與處理出任意兩點間的距離 
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 310*2
using namespace std;
int top[MAXN];
int n,s,tot,bns,ans;
int map[MAXN][MAXN];
int to[MAXN],cap[MAXN],net[MAXN],head[MAXN];
int dad[MAXN],deep[MAXN],siz[MAXN],length[MAXN];
void add(int u,int v,int w){
    to[++tot]=v;cap[tot]=w;net[tot]=head[u];head[u]=tot;
}
void dfs(int now){
    siz[now]=1;
    deep[now]=deep[dad[now]]+1;
    for(int i=head[now];i;i=net[i])
        if(dad[now]!=to[i]){
            dad[to[i]]=now;
            length[to[i]]=length[now]+cap[i];
            dfs(to[i]);
            siz[now]+=siz[to[i]];
        }
}
void dfs1(int now){
    int t=0;
    if(!top[now])    top[now]=now;
    for(int i=head[now];i;i=net[i])
        if(dad[now]!=to[i]&&siz[to[i]]>siz[now])
            t=to[i];
    if(t){
        top[t]=top[now];
        dfs1(t); 
    }
    for(int i=head[now];i;i=net[i])
        if(dad[now]!=to[i]&&t!=to[i])
            dfs1(to[i]);
}
int lca(int x,int y){
    for(;top[x]!=top[y];){
        if(deep[top[x]]<deep[top[y]])
            swap(x,y);
        x=dad[top[x]];
    }
    if(deep[x]>deep[y])    swap(x,y);
    return x;
}
int dfs2(int u,int v,int now){
    int fa=lca(u,v),cns=0x7f7f7f7f;
    for(int i=u;i!=fa;i=dad[i])
        cns=min(cns,map[now][i]);
    for(int i=v;i!=fa;i=dad[i])
        cns=min(cns,map[now][i]);
    cns=min(cns,map[now][fa]);
    return cns;
}
void work(int a,int b){
    bns=0;
    if(a==b){
        for(int i=1;i<=n;i++)
            bns=max(bns,map[i][a]);
        ans=min(ans,bns);
        return ;
    }
    if(map[a][b]>s)    return;
    for(int i=1;i<=n;i++)
        bns=max(bns,dfs2(a,b,i));
    ans=min(ans,bns);
    return ;
}
int main(){
    scanf("%d%d",&n,&s);
    memset(map,0x3f,sizeof(map));
    for(int i=1;i<n;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
        map[u][v]=map[v][u]=w;
    }
    for(int i=1;i<=n;i++)    map[i][i]=0;
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i!=j&&i!=k&&j!=k)
                    map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
    dfs(1);dfs1(1);
    ans=0x7f7f7f7f;
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            work(i,j);
    cout<<ans;
}
84 技術分享圖片
/*
可以想象出,這個樹網的核一定在這棵樹的直徑上(不一定對) 
因為n很小,可以與處理出任意兩點間的距離 
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 310*2
using namespace std;
int top[MAXN];
int n,s,tot,bns,ans;
int map[MAXN][MAXN];
int to[MAXN],cap[MAXN],net[MAXN],head[MAXN];
int dad[MAXN],deep[MAXN],siz[MAXN],length[MAXN];
void add(int u,int v,int w){
    to[++tot]=v;cap[tot]=w;net[tot]=head[u];head[u]=tot;
}
void work(int a,int b){
    bns=0;
    if(a==b){
        for(int i=1;i<=n;i++)
            bns=max(bns,map[i][a]);
        ans=min(ans,bns);
        return ;
    }
    if(map[a][b]>s)    return;
    for(int i=1;i<=n;i++)
        bns=max(bns,(map[i][a]+map[i][b]-map[a][b])/2);
    ans=min(ans,bns);
    return ;
}
int main(){
    scanf("%d%d",&n,&s);
    memset(map,0x3f,sizeof(map));
    for(int i=1;i<n;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
        map[u][v]=map[v][u]=w;
    }
    for(int i=1;i<=n;i++)    map[i][i]=0;
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i!=j&&i!=k&&j!=k)
                    map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
    ans=0x7f7f7f7f;
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            work(i,j);
    cout<<ans;
}
100

洛谷 P1099 樹網的核