NOIP2007 樹網的核 [dfs] [資料結構] [樹的直徑]
樹網的核
(core.pas/c/cpp)
【問題描述】
設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的距離,即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。
【輸入】
輸入檔案core.in包含n行:
第1行,兩個正整數n和s,中間用一個空格隔開。其中n為樹網結點的個數,s為樹網的核的長度的上界。設結點編號以此為1,2,……,n。
從第2行到第n行,每行給出3個用空格隔開的正整數,依次表示每一條邊的兩個端點編號和長度。例如,“2 4 7”表示連線結點2與4的邊的長度為7。
所給的資料都是爭取的,不必檢驗。
【輸出】
輸出檔案core.out只有一個非負整數,為指定意義下的最小偏心距。
【輸入輸出樣例】
【輸入輸出樣例1】
core.in Core.out
5 2
1 2 5
2 3 2
2 4 4
2 5 3 5
【輸入輸出樣例2】
core.in core.out
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3 5
【限制】
40%的資料滿足:5<=n<=15
70%的資料滿足:5<=n<=80
100%的資料滿足:5<=n<=300,0<=s<=1000。邊長度為不超過1000的正整數
Floyed的O(n^3)太暴力,但是考慮到是07年的題,但是肯定過不了。。
那麼O(n^2)的做法列舉直徑上的子路徑,然後求最大深度。
O(nlogn)的話,我用優先佇列維護當前最大可以優化的路徑,那麼維護dis陣列,表示當前子樹的最長路徑。
網上看到另一種做法,也是O(nlogn),先找出直徑,然後對於直徑上的點可以用資料結構來維護。。
我的做法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 305;
struct Edge
{
int to,next;
int val;
}edge[maxn<<1];
int head[maxn];
int maxedge;
inline void addedge(int u,int v,int c)
{
edge[++maxedge] = (Edge) { v,head[u],c };
head[u] = maxedge;
edge[++maxedge] = (Edge) { u,head[v],c };
head[v] = maxedge;
}
int n,s;
void init()
{
scanf("%d%d",&n,&s);
memset(head,-1,sizeof(head));
maxedge=-1;
for(int i=1;i<n;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
addedge(u,v,c);
}
}
void Find(int,int);
int dis[maxn];
bool chain[maxn]; // to mark if is on the chain
int end1,end2;
void dfs1(int u,int father,int deep,int &end)
{
dis[u] = deep;
if(!end || dis[end]<dis[u]) end=u;
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v==father) continue;
dfs1(v,u,deep+edge[i].val,end);
}
}
bool dfs2(int u,int father,int end,int half)
{
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v==father) continue;
if(v==end || dfs2(v,u,end,half))
{
if(half>=dis[u] && half<dis[v]) Find(u,v);
return true;
}
}
return false;
}
void dfs3(int u,int father)
{
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v==father) continue;
dfs3(v,u);
smax(dis[u],dis[v]+edge[i].val);
}
}
bool dfs4(int u,int father,int end)
{
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v==father) continue;
if(v==end || dfs4(v,u,end))
{
chain[u]=true;
return true;
}
}
return false;
}
int S,T;
struct Info
{
int u,from,val,id;
bool operator < (const Info t) const { return val < t.val; }
};
priority_queue <Info> que;
void Find(int u,int v)
{
int ans=INF,tmp=0;
memset(dis,0,sizeof(dis));
dfs3(v,-1); // only node as v
for(int i=head[v];~i;i=edge[i].next) smax(tmp,dis[edge[i].to]+edge[i].val);
smin(ans,tmp);
memset(dis,0,sizeof(dis));
dfs3(u,-1); S=T=u; tmp=0;
for(int i=head[u];~i;i=edge[i].next)
if(chain[edge[i].to]) que.push((Info){edge[i].to,u,dis[edge[i].to]+edge[i].val,i});
else smax(tmp,dis[edge[i].to]+edge[i].val);
int totlens=0;
while(!que.empty())
{
Info now = que.top(); que.pop();
if(now.from^S && now.from^T)
{
smax(tmp,now.val);
continue;
}
if(totlens+edge[now.id].val>s)
{
smin(ans,max(now.val,tmp));
printf("%d",ans);
exit(0);
}
for(int i=head[now.u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v==now.from) continue;
if(chain[v]) que.push((Info){v,now.u,dis[v]+edge[i].val,i}); // dis is settled to be the left max_dis
else smax(tmp,dis[v]+edge[i].val);
}
if(now.from==S) S=now.u;
else T=now.u;
totlens+=edge[now.id].val;
}
}
void work()
{
end1=end2=0;
dfs1(1,-1,0,end1);
dfs1(end1,-1,0,end2);
dfs4(end1,-1,end2); chain[end1]=true; chain[end2]=true;
int half=dis[end2]>>1;
dfs2(end1,-1,end2,half);
}
int main()
{
freopen("core.in","r",stdin);
freopen("core.out","w",stdout);
init();
work();
return 0;
}