樹形DP,顧名思義就是在樹上進行dp,dp的時候要充分利用樹的性質,注意考慮全部能轉移的節點c++
例:樹的直徑spa
給你一顆點數爲n的樹,讓你求這棵樹的直徑是多少,也就是求最長的兩個點之間的距離。code
N<=100000blog
inci
7 1 6 13 6 3 9 3 5 7 4 1 3 2 4 20 4 7 2
outget
52
兩種作法,複雜度都是O(N)it
1.dfs(或者bfs)ast
先從任意一個點跑一遍dfs,而後遍歷每個點找到距離這個點最遠的,而後再從這個點開始跑一邊dfs,再找到距離這個點最遠的,那麼這兩個點之間的距離就是樹的直徑class
不能跑負邊權的樹遍歷
代碼:
#include<bits/stdc++.h> using namespace std; const int N=2333333,inf=0x3f3f3f3f; int n; int dis[N]; int head[N],cnt; struct edge { int to,dis,nxt; }edg[N<<1]; inline void add(int u,int v,int w) { edg[++cnt].dis=w; edg[cnt].to=v; edg[cnt].nxt=head[u]; head[u]=cnt; } void dfs(int x,int fa) { for(int i=head[x];i;i=edg[i].nxt) { int v=edg[i].to; if(v==fa) continue; dis[v]=dis[x]+edg[i].dis; dfs(v,x); } } int main() { int n; cin>>n; for(int i=1;i<=n-1;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } memset(dis,0x3f,sizeof(dis)); dis[1]=0; dfs(1,0); int s=0,maxn=-1; for(int i=1;i<=n;i++) { if(dis[i]>maxn&&dis[i]!=inf) { maxn=dis[i]; s=i; } } memset(dis,0x3f,sizeof(dis)); dis[s]=0; dfs(s,0); for(int i=1;i<=n;i++) { if(dis[i]>maxn&&dis[i]!=inf) { maxn=dis[i]; s=i; } } cout<<maxn; }
2.樹形dp
設f[i]表示i這個點到子樹裏面的最遠點是多遠的,而後對於每個點u求出以這個點爲根的最遠路徑距離,直接找{f[son_i]+edge_i}的前兩大值加起來便可。而後再在每個點構成的答案裏面取最大值就是全局的最優值了。
代碼:
#include<bits/stdc++.h> using namespace std; const int N=2333333,inf=0x3f3f3f3f; int n; int f[N]; int head[N],cnt; int ans; struct edge { int to,dis,nxt; }edg[N<<1]; inline void add(int u,int v,int w) { edg[++cnt].dis=w; edg[cnt].to=v; edg[cnt].nxt=head[u]; head[u]=cnt; } void dp(int x,int fa) { int maxn=0; for(int i=head[x];i;i=edg[i].nxt) { int v=edg[i].to; if(v==fa) continue; dp(v,x); ans=max(ans,maxn+edg[i].dis+f[v]); f[x]=max(f[x],f[v]+edg[i].dis); maxn=max(maxn,f[v]+edg[i].dis); } } int main() { int n; cin>>n; for(int i=1;i<=n-1;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } dp(1,0); cout<<ans; }
例:沒有上司的舞會
這道題算是樹形dp中比較經典並且比較簡單的題了
首先注意到對於每個節點,能夠選或者不選
若是選的話,他的全部兒子都不能選
若是不選的話,他的全部兒子能夠選也能夠不選,顯然要在選或者不選裏面取最大的
設f[i][0/1]表示當前在第i個點,這個點 不選/選 的最大價值
因而狀態轉移方程
f[i][1]=∑f[son[i]][0]
f[i][0]=∑max(f[son[i]][0],f[son[i]][1]);
代碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } struct edge { int to,nxt; }edg[6050]; int head[6050],cnt,in[6050]; int n,r[6050],f[6050][3]; int root; inline void add(int u,int v) { edg[++cnt].to=v; edg[cnt].nxt=head[u]; head[u]=cnt; } void dp(int now) { f[now][1]=r[now]; for(int i=head[now];i;i=edg[i].nxt) { int v=edg[i].to; dp(v); f[now][1]+=f[v][0]; f[now][0]+=max(f[v][0],f[v][1]); } } int main() { n=read(); for(int i=1;i<=n;i++) r[i]=read(); for(int i=1;i<=n-1;i++) { int v=read(),u=read();in[v]++; add(u,v); } for(int i=1;i<=n;i++) { if(in[i]==0) root=i; } dp(root); cout<<max(f[root][0],f[root][1]); }