HIHO #1055 : 刷油漆(樹形dp 入門)
阿新 • • 發佈:2019-02-19
入門的話還是看09《淺談幾類揹包問題》 = =。
設dp[u][j] : 表示對於,以編號是u的節點為根的子樹,選擇不超過j個節點,可以獲得的最大的價值。
狀態的轉移:dp[u][j]=max( dp[u][j] , dp[v_i][j-1] ) + value[u](v_i是u的兒子,j>=1)
方程是類似完全揹包的,兒子節點,使用dfs遍歷一遍。
選擇兒子節點的話,就必須選擇父親節點。假設存在u--v的一條邊,
如果選擇了v那麼u是必須選擇的,實現這一要求的是,我們在dfs訪問v的時候,如何設定v的初始狀態?
可以這樣設定,首先v的狀態等於他的父親u的狀態,dp[v][j=0...m-1]=dp[u][j=0...m-1],(m是最多能選擇的節點的上限),然後強制把節點v加入的狀態裡,dp[v][j=0...m-1] = dp[u][j=0...m-1] +
value[v]
最後dfs回溯回來,也就是遍歷完v的子孫節點後,更新v 的父親。 dp[u][j] = max(dp[u][j], dp[v][j-1])(1<=j<=m)
程式碼實現加一個虛擬根節點0
答案就是dp[0][m];
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<string> #include<cmath> #include<queue> #include<map> #include<set> #include<cstdlib> #include<vector> using namespace std; #define cl(a,b) memset(a,b,sizeof(a)) #define LL long long #define pb push_back #define gcd __gcd #define For(i,j,k) for(int i=(j);i<k;i++) #define lowbit(i) (i&(-i)) #define _(x) printf("%d\n",x) const int maxn = 1e2+10; const int inf = 1 << 28; int value[maxn]; vector<int> G[maxn]; int dp[maxn][maxn]; void dfs(int u,int M,int fa){ if(M<=0)return ; for(int i=0;i<G[u].size();i++){ int v = G[u][i]; if(fa==v)continue; for(int j=0;j<M;j++){ dp[v][j]=dp[u][j]+value[v]; } dfs(v,M-1,u); for(int j=1;j<=M;j++){ dp[u][j]=max(dp[u][j],dp[v][j-1]); } } } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&value[i]); value[0]=0; int x,y; for(int i=0;i<n-1;i++){ scanf("%d%d",&x,&y); G[x].pb(y); G[y].pb(x); } G[0].pb(1); dfs(0,m,-1); printf("%d\n",dp[0][m]); return 0; }