標籤:樹 dp 樹形dp 背包問題
連結:http://poj.org/problem?id=2486
題意:一棵(蘋果)樹,樹上有N個結點(N<=100),起點是結點1。每個結點上有若干個蘋果,我可以進行K步操作(K<=200),每次操作是從當前結點移動到相鄰的結點,並且到了相鄰的結點以後會吃掉上面的所有蘋果並且蘋果不再長出來,相鄰是指兩個結點之間有邊相連。問在K步操作之後最多可以吃掉多少個蘋果。
思路:剛入手的時候覺得是一般的樹形背包問題,dp[i][j]代表的是以i為根的子樹中走j個結點所能吃到的蘋果數,來進行狀態轉移,但這樣轉移的話每次的轉移消耗就是2(一來一回),但是有可能出現的情況是不回到起點直接到某個點就停下不向前走。
這樣上面的狀態轉移就不能滿足要求。所以要擴充為dp[i][j][k]。
k=0,代表的是最後停在了i的子樹中不回到結點i;k=1,代表的是在i的子樹中走了j個結點並且最後回到了結點j。
狀態轉移方程:
dp[u][i+2][1]=max(dp[u][i+2][1],dp[u][i-j][1]+dp[v][j][1]);表示回到根結點的情況可以通過回到子結點的情況推得。
dp[u][i+1][0]=max(dp[u][i+1][0],dp[u][i-j][1]+dp[v][j][0]);表示不回到根結點的情況可以通過將原來回到根結點的情況由不回到子結點的情況更新得到。
dp[u][i+2][0]=max(dp[u][i+2][0],dp[u][i-j][0]+dp[v][j][1]);表示不回到根結點的情況可以通過將原來不回到根結點的情況由回到子結點的情況更新得到。
這個分組背包表示一種思想上的分組,因為dp[i][j][0]與dp[i][j][1]代表的兩種情況是矛盾的,一定是不能同時出現。最後更新回到根結點時,所有狀態中的最大值即為答案。
代碼:
#include <algorithm>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <ctime>#include <ctype.h>#include <iostream>#include <map>#include <queue>#include <set>#include <stack>#include <string>#include <vector>#define eps 1e-8#define INF 0x3fffffff#define maxn 105#define maxm 205#define PI acos(-1.0)#define seed 31//131,1313typedef long long LL;typedef unsigned long long ULL;using namespace std;int head[maxn],top,ans,dp[maxn][maxm][2],N,K,x,y,a[maxn];void init(){ memset(head,-1,sizeof(head)); memset(dp,0,sizeof(dp)); memset(a,0,sizeof(a)); top=0; ans=0;}struct Edge{ int v; int next;} edge[maxn*2];void add_edge(int u,int v){ edge[top].v=v; edge[top].next=head[u]; head[u]=top++;}void dfs(int u,int f){ dp[u][0][0]=dp[u][0][1]=a[u]; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(v==f) continue; dfs(v,u); for(int i=K;i>=0;i--) { for(int j=i;j>=0;j--) { dp[u][i+2][1]=max(dp[u][i+2][1],dp[u][i-j][1]+dp[v][j][1]); dp[u][i+1][0]=max(dp[u][i+1][0],dp[u][i-j][1]+dp[v][j][0]); dp[u][i+2][0]=max(dp[u][i+2][0],dp[u][i-j][0]+dp[v][j][1]); } } }}int main(){ while(~scanf("%d%d",&N,&K)) { init(); for(int i=1; i<=N; i++) scanf("%d",&a[i]); for(int i=1;i<=N-1;i++) { scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x); } dfs(1,1); int ans=0; for(int i=0;i<=K;i++) { if(dp[1][i][0]>ans) ans=dp[1][i][0]; if(dp[1][i][1]>ans) ans=dp[1][i][1]; } printf("%d\n",ans); } return 0;}
POJ 2486 Apple Tree 樹形DP+分組背包