洛谷 P1099 樹網的核
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),v∈V}
任務:對於給定的樹網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
下面的圖給出了樹網的一個實例。圖中,A-BA−B與A-CA−C是兩條直徑,長度均為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 155≤n≤15
70\%70%的數據滿足:5 \le n \le 805≤n≤80
100\%100%的數據滿足:5 \le n \le 300,0 \le s \le 10005≤n≤300,0≤s≤1000。邊長度為不超過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 樹網的核