1. 程式人生 > 實用技巧 >樹上dp_學習筆記

樹上dp_學習筆記

1.AcWing.1073 樹的中心:

題意:給你一個樹,包含n個結點,和n-1條帶權無向邊。請你在樹中找到一個點,使得該點到樹中其他結點的最遠距離最近,結果輸出所求點到樹中其他結點的最遠距離。

解法:首先分析題目,其實是要我們把每一個點到其他點的最長距離求出來,再求一個其中最短的就可以了,我們來分析一下每一個點可以再樹上怎麼走,其實就是向上和向下走。我們用d1[u]表示u結點向下走的最長路徑,d2[u]表示u結點向下走的次長路徑,p1[u]表示u結點向下走最長路徑的兒子節點,up[u]表示u節點向上走的最長路徑。

以求得向下走的最長路徑d1[u]和次長路徑d2[u]。那麼問題的關鍵就在於求向上走的最長路徑up[u]了。

這個時候我們把結點v當做當前結點,分析其父親結點u。如果v是u的p1結點,那麼up[v]=max(up[u],d2[u])+edge[i].w;如果v不是u的p1結點,那麼up[v]=max(up[u],d1[u])+edge[i].w。另外要注意看自己是不是葉結點(即有沒有向下的孩子,如果沒有的話d1=d2=0)

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define
endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1e5+5; int head[maxn],tot; struct E{ int to,next,w; }edge[maxn
<<1]; void add(int u,int v,int w){ edge[tot].to=v; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot++; } bool is_leaf[maxn]; int n,p1[maxn],d1[maxn],d2[maxn],up[maxn]; int dfs_d(int u,int fa){ d1[u]=d2[u]=-INF; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==fa) continue; int d=dfs_d(v,u)+edge[i].w; if(d>=d1[u]){ d2[u]=d1[u];d1[u]=d; p1[u]=v; } else if(d>d2[u]) d2[u]=d; } if(d1[u]==-INF){ d1[u]=d2[u]=0; is_leaf[u]=true; } return d1[u]; } void dfs_up(int u,int fa){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==fa) continue; if(p1[u]==v){ up[v]=max(d2[u],up[u])+edge[i].w; } else up[v]=max(up[u],d1[u])+edge[i].w; dfs_up(v,u); } } int main(){ scanf("%d",&n);mem(head,-1); rep(i,1,n-1){ int u,v,w;scanf("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); } dfs_d(1,-1); dfs_up(1,-1); int res=d1[1]; for(int i=2;i<=n;i++){ if(is_leaf[i]) res=min(res,up[i]); else res=min(res,max(up[i],d1[i])); } cout<<res<<endl; }
View Code