POJ2486 Apple Tree(树形DP)
题目大概是一棵树,每个结点都有若干个苹果,求从结点1出发最多走k步最多能得到多少个苹果。
考虑到结点可以重复走,容易想到这么个状态:
- dp[u][k][0]表示在以结点u为根的子树中走k步且必须返回u能得到的最多苹果
- dp[u][k][1]表示在以结点u为根的子树中走k步且可以不返回u能得到的最多苹果
- 单纯这样转移又是指数级的时间复杂度,所以又是树上背包了
转移就是:
- dp[u][k][0]=max(dp[u][k][0],dp[v][k'][0]+dp[u][k-k'-2][0])(v是u当前的孩子),对于必须返回的就是从u走一步到v,分k‘个给当前的子树v走并走回v,最后再走一步返回u
- dp[u][k][1]=max(dp[u][k][1],dp[v][k'][1]+dp[u][k-k'-1][0])(v是u当前的孩子),之前的子树走完回到u,走一步到v,最后不返回地在当前的子树v中走k‘步
不过这样提交WA。。然而看不出哪里有错。。无奈看别人代码,发现转移少考虑了一种情况——
- dp[u][k][1]=max(dp[u][k][1],dp[v][k'][0]+dp[u][k-k'-2][1])(v是u当前的孩子),从u走一步到v,分配k'步给当前子树v走并返回v,再走一步回到u,最后不返回地向之前的子树走
真的想不到。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 111 6 struct Edge{ 7 int v,next; 8 }edge[MAXN<<1]; 9 int NE,head[MAXN]; 10 void addEdge(int u,int v){ 11 edge[NE].v=v; edge[NE].next=head[u]; 12 head[u]=NE++; 13 } 14 int d[MAXN][222][2]; 15 int n,m,apple[MAXN]; 16 void dp(int u,int fa){ 17 d[u][0][0]=d[u][0][1]=apple[u]; 18 for(int i=head[u]; i!=-1; i=edge[i].next){ 19 int v=edge[i].v; 20 if(v==fa) continue; 21 dp(v,u); 22 for(int j=m; j>0; --j){ 23 for(int k=0; k<j; ++k){ 24 if(d[v][k][1]==-1 || d[u][j-k-1][0]==-1) continue; 25 d[u][j][1]=max(d[u][j][1],d[v][k][1]+d[u][j-k-1][0]); 26 } 27 for(int k=0; k<j-1; ++k){ 28 if(d[v][k][0]==-1 || d[u][j-k-2][1]==-1) continue; 29 d[u][j][1]=max(d[u][j][1],d[v][k][0]+d[u][j-k-2][1]); 30 } 31 for(int k=0; k<j-1; ++k){ 32 if(d[v][k][0]==-1 || d[u][j-k-2][0]==-1) continue; 33 d[u][j][0]=max(d[u][j][0],d[v][k][0]+d[u][j-k-2][0]); 34 } 35 } 36 } 37 } 38 int main(){ 39 int a,b; 40 while(~scanf("%d%d",&n,&m)){ 41 NE=0; 42 memset(head,-1,sizeof(head)); 43 for(int i=1; i<=n; ++i) scanf("%d",apple+i); 44 for(int i=1; i<n; ++i){ 45 scanf("%d%d",&a,&b); 46 addEdge(a,b); addEdge(b,a); 47 } 48 memset(d,-1,sizeof(d)); 49 dp(1,1); 50 int res=-1; 51 for(int i=0; i<=m; ++i){ 52 res=max(res,d[1][i][1]); 53 } 54 printf("%d\n",res); 55 } 56 return 0; 57 }