1. 程式人生 > 實用技巧 >洛谷 【XR-3】核心城市(樹的直徑,樹形dp)

洛谷 【XR-3】核心城市(樹的直徑,樹形dp)

傳送門


解題思路

先考慮一個點。肯定是在樹的直徑的中間rt。

樹的直徑就是樹上距離最遠的兩個點的路徑。

對於樹的直徑求法,常用的有兩種。

  1. 兩遍bfs或dfs,從任意一個點開始,找到距離這個點最遠的點i,再從i開始,找到距離i這個點距離最遠的點j,則i、j就是樹的直徑的兩個端點(證明略)。
  2. 樹形dp也可以求。

再推廣到n個點。以rt為根,樹形dp,dis[i]表示以i為根的子樹的深度,即把城市i作為核心城市後,子樹i中距離i最遠的點到核心城市群的距離。

然後把dis排序,刪去最大的前k個,這k個就是核心城市群。然後第k+1大的dis再+1就是最終的答案。

為什麼要+1呢?因為第k+1大的dis對應的城市到核心城市群還有1的距離,所以要加1.

AC程式碼

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath> 
#include<algorithm>
using namespace std;
const int maxn=100005;
int n,k,dis[maxn][3],diss[maxn],cnt,p[maxn],d1,d2,rt;
struct node{
    int v,next;
}e[maxn*2];
void insert(int
u,int v){ cnt++; e[cnt].next=p[u]; e[cnt].v=v; p[u]=cnt; } int dfs(int u,int id){ int ans; queue<int> q; dis[u][id]=0; q.push(u); while(!q.empty()){ u=q.front(); q.pop(); if(q.empty()){ ans=u; } for(int i=p[u];i!=-1
;i=e[i].next){ int v=e[i].v; if(!dis[v][id]){ dis[v][id]=dis[u][id]+1; q.push(v); } } } return ans; } void findd(){ d1=dfs(1,0); d2=dfs(d1,1); for(int i=1;i<=n;i++){ if(abs(dis[i][0]-dis[i][1])<=1){ rt=i; break; } } } void dfs2(int u,int fa){ for(int i=p[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v==fa) continue; dfs2(v,u); diss[u]=max(diss[u],diss[v]+1); } } int main(){ memset(p,-1,sizeof(p)); cin>>n>>k; for(int i=1;i<n;i++){ int u,v; cin>>u>>v; insert(u,v); insert(v,u); } findd(); dfs2(rt,-1); sort(diss+1,diss+n+1); cout<<diss[n-k]+1<<endl; return 0; }