[Acwing藍橋杯DP] 1078. 旅遊規劃
阿新 • • 發佈:2022-04-02
題目大意: 求一個樹上,直徑上的所有的點
資料範圍:節點n 1<=n<=2e5
範圍很大 要求時間複雜度控制在 lnn 以內
分析:
這是一個樹形DP,基於樹的直徑,求樹所有直徑上的點
整體思路:
1、先通過樹形dp求出每個點往下走的最大長度和次大長度,並且更新整棵樹的最大路徑maxx
2、往下走最大值(第一步已求) + 往上走最大值 == maxx,即在直徑上
這個題的難點就是:
首先要理解為什麼一個節點既要往上求,又要往下求?
因為第一次dfs時候,回溯時候,是從下往上的,先求下面的點,再上邊的
藉助y總畫的圖容易理解
還有就是理解那三個陣列:d1往下最大值 d2往下的次大值 up是往上的最大值(實際上就是配合p陣列,求最大值)
還有就是p陣列的理解: p[u]=j :從u下去是j 。
比如一個點 i 往下的最大值和次大值是確定的,往上不確定,往上是遞迴,這裡的往上也不是嚴格意義的往上,是相較與上次遞歸向下而言的,也有可能向下。
這個p陣列就是為了防止向下時候最大值算兩邊,如果是最大值就取次小值。反正這個up值是一直取最大的。
看程式碼:
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10 ,M=2*N;
int n;
int h[N],e[M],ne[M],idx;
int d1[N],d2[N],p[N],up[N];
int maxx;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs1(int u,int father)
{
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(j!=father)
{
dfs1(j,u);
int d=d1[j]+1;
if(d>d1[u])
{
d2[u]=d1[u];
d1[u]=d;
p[u]=j;
}else if(d>d2[u])
{
d2[u]=d;
}
}
}
maxx=max(maxx,d1[u]+d2[u]);
}
void dfs2(int u,int father)
{
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(j!=father)
{
up[j]=up[u]+1;
if(p[u]==j)
{
up[j]=max(up[j],d2[u]+1);
}else
{
up[j]=max(up[j],d1[u]+1);
}
dfs2(j,u);
}
}
}
int main()
{
memset(h,-1,sizeof (h));
cin>>n;
for(int i=0;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
dfs1(0,-1);
dfs2(0,-1);
for(int i=0;i<n;i++)
{
int a[3]={d1[i],d2[i],up[i]};
sort(a,a+3);
if(a[2]+a[1]==maxx)
{
cout<<i<<endl;
}
}
return 0;
}
END!!!