網線長度
實驗室裏原先有一臺電腦(編號爲1),最近又爲實驗室購置了N-1臺電腦,編號爲2到N。每臺電腦都用網線連接到一臺先前安裝的電腦上。我們希望知道第i臺電腦到其他電腦的最大網線長度。
提示: 樣例輸入對應這個圖,從這個圖中你可以看出,距離1號電腦最遠的電腦是4號電腦,他們之間的距離是3。 4號電腦與5號電腦都是距離2號電腦最遠的點,故其答案是2。5號電腦距離3號電腦最遠,故對於3號電腦來說它的答案是3。同樣的我們可以計算出4號電腦和5號電腦的答案是4.
輸入文件包含多組測試數據。對於每組測試數據,第一行一個整數N (N<=10000),接下來有N-1行,每一行兩個數,對於第i行的兩個數,它們表示與i號電腦連接的電腦編號以及它們之間網線的長度。網線的總長度不會超過10^9,每個數之間用一個空格隔開
對於每組測試數據輸出N行,第i行表示i號電腦的答案 (1<=i<=N)
sample input:
5
1 1
2 1
3 1
1 1
sample output:
3
2
3
4
4
思路
- 題目說了這麼多,其實就是構建一棵樹,然後尋找離每個節點最遠的結點
- 所以起初我使用樹形DP的做法來解決這個問題,把無根樹化爲有根樹來處理
- 比如對於上面這棵樹,要求點4的最長距離,可以將樹tree1分解成以節點4爲根節點的tree2和以節點4的父節點爲根節點的樹tree1-tree2;這樣的話,對於整棵樹來說,tree2中距離節點2最遠的距離記作l1,tree1-tree2中最遠的距離記作l2,那麼最終的答案就是max(l1,l2+1).
- 利用一個二維數組來記錄。f[i][0]表示頂點爲i的子樹距離頂點i的最長距離;f[i][1]表示i的父節點的子樹-i子樹的最長距離+1(i與i的父節點距離)
- 所有的f[i][0]都做一次dfs求每一個節點到葉子節點的最長距離,然後從父節點遞推到子節點計算所有的f[i][1]
- 這種經典樹形DP的實現代碼如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct edge
{
int v,w,next;
}edge[10111];
int head[10111];
int n;
void add(int u,int v,int w)
{
edge[n].v=v;
edge[n].w=w;
edge[n].next=head[u];
head[u]=n++;
}
long long path[3][10011];
int visit[10011];
void dfs(int u,int far)
{
long long mx0=0,mx1=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==far) continue;
dfs(v,u);
if(mx0<=path[0][v]+edge[i].w)
{
mx1=mx0;
mx0=path[0][v]+edge[i].w;
visit[u]=v;
}
else if(mx1<path[0][v]+edge[i].w) mx1=path[0][v]+edge[i].w;
else if(mx1<path[1][v]+edge[i].w) mx1=path[1][v]+edge[i].w;
}
path[0][u]=mx0;
path[1][u]=mx1;
}
void dfss(int u,int far)
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==far) continue;
if(visit[u]==v) path[2][v]=max(path[1][u]+edge[i].w,path[2][u]+edge[i].w);
else path[2][v]=max(path[0][u]+edge[i].w,path[2][u]+edge[i].w);
dfss(v,u);
}
}
int main()
{
int a,b,c;
while (~scanf("%d",&c))
{
n=0;
memset(head,-1,sizeof(head));
for(int i=2;i<=c;i++)
{
scanf("%d%d",&a,&b);
add(i,a,b);
add(a,i,b);
}
memset(path,0,sizeof(path));
dfs(1,1);
dfss(1,1);
for(int i=1;i<=c;i++)
cout<<max(path[0][i],path[2][i])<<endl;
}
return 0;
}
- 但是這樣寫出現了TLE的問題,可能是測試數據卡的比較嚴,所以採用另一種思路,對於樹中任意頂點進行一遍bfs,找到一個距離它最遠的頂點作爲端點1(肯定是一個葉子節點),然後再從端點進行bfs,找到距離最遠的點,肯定是一個葉子節點,我們認爲他是端點2
- 這樣的話以這兩個端點爲起點對這棵樹中所有的點都進行bfs,記錄下距離最終的結果就是兩個端點到這個點的距離的較大值
- 我們將樹中最遠的兩個葉子節點之間的距離成爲樹的直徑,這兩個點被稱爲端點;整棵樹中任意兩個節點之間的距離都小於等於樹的直徑
#include<iostream>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int N=500001;
struct edge
{
int u,v;
int w;
int next;
}e[N];
int a[N],visit[N],dis[N];
int tot;
int p1,p2;
int ans[2][N];//0,1
void add(int u,int v,int w)
{
tot++;
e[tot].u=u;
e[tot].v=v;
e[tot].w=w;
e[tot].next=a[u];
a[u]=tot;
}
void bfs(int node,int flag)
{
memset(visit,0,sizeof(visit));
memset(dis,0,sizeof(dis));
int length=0;
queue<int> q;
q.push(node);
visit[node]=true;
while(!q.empty())
{
int temp=q.front();
q.pop();
for(int i=a[temp];i!=-1;i=e[i].next)
{
int nex=e[i].v;
if(!visit[nex])
{
dis[nex]=dis[temp]+e[i].w;
if(dis[nex]>length)
{
length=dis[nex];
if(flag==true)
p1=nex;
else p2=nex;
}
visit[nex]=1;
q.push(nex);
}
}
}
}
void bfss(int node,int flag)
{
memset(visit,0,sizeof(visit));
queue<int> q;
q.push(node);
visit[node]=true;
while(!q.empty())
{
int temp=q.front();
q.pop();
for(int i=a[temp];i!=-1;i=e[i].next)
{
int nex=e[i].v;
if(!visit[nex])
{
ans[flag][nex]=ans[flag][temp]+e[i].w;
visit[nex]=true;
q.push(nex);
}
}
}
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(ans,0,sizeof(ans));
tot=0;
p1=0;p2=0;
int u,v,w;
for(int i=1;i<=n;i++)
a[i]=-1;
for(int i=2;i<=n;i++)
{
scanf("%d%d",&v,&w);
add(i,v,w);
add(v,i,w);
}
bfs(1,1);
bfs(p1,2);
bfss(p1,0);
bfss(p2,1);
for(int i=1;i<=n;i++)
printf("%d\n",max(ans[0][i],ans[1][i]));
}
return 0;
}