POJ 2486 Apple Tree(树形DP+分组背包)
http://poj.org/problem?id=2486
应该是做的第二道树形DP+背包问题
题意:有颗苹果树n个节点,n-1条边,每个节点上有一定数量的苹果,每经过一条边花费一个单位时间,一共给了T个单位时间
一棵树,从一个节点到另一个节点,如果不是父子关系的话,就要先返回父节点,再到另一个节点;总共给了T个单位时间,最后
不一定再哪个节点,所以还有访问完子节点是否返回根节点的问题,为了表示是否返回,定义状态dp[i][j][0]表示不返回,
dp[i][j][1]表示返回。这题就应该是这两点不好想吧。考虑好这两点,其它的就是考虑好转移过程了,理解好背包问题,不算难。
代码:
//注意的是加边的时候,没说谁是根 #include<iostream> #include<cstdio> #include<cstring> #include<string> #define nMAX 120 using namespace std; int num[nMAX],val[nMAX],head[nMAX],s_edge; int dp[nMAX][nMAX*2][2]; int n,T; int max(int a,int b) { return a>b?a:b; } int min(int a,int b) { return a<b?a:b; } struct Edge { int v,nxt; }edge[nMAX*2]; void addedge(int u,int v) { s_edge++; edge[s_edge].v=v; edge[s_edge].nxt=head[u]; head[u]=s_edge; } void dfs1(int u,int fa) { num[u]=0; for(int e=head[u];e;e=edge[e].nxt) { int v=edge[e].v; if(v==fa) continue; dfs1(v,u); num[u]+=(num[v]+2); } num[u]=min(num[u],T); } void dfs2(int u,int fa) { int i,j; for(int e=head[u];e;e=edge[e].nxt) { int v=edge[e].v; if(v==fa) continue; dfs2(v,u); for(i=num[u];i>=0;i--) for(j=0;j<=num[v];j++) { //不能返回的 if(i-j-1>=0) { dp[u][i][0]=max(dp[u][i][0],dp[u][i-j-1][1]+dp[v][j][0]); dp[u][i][0]=max(dp[u][i][0],dp[u][i-j-1][1]+dp[v][j][1]); } if(i-j-2>=0) dp[u][i][0]=max(dp[u][i][0],dp[u][i-j-2][0]+dp[v][j][1]); //能返回的 if(i-j-2>=0) dp[u][i][1]=max(dp[u][i][1],dp[u][i-j-2][1]+dp[v][j][1]); } } } int main() { int i,j,k; while(~scanf("%d%d",&n,&T)) { for(i=1;i<=n;i++) scanf("%d",&val[i]); memset(head,0,sizeof(head)); s_edge=0;//开始这里怎么就写成edge++了呢??RE了好几次。。。 for(i=1;i<n;i++) { scanf("%d%d",&j,&k); addedge(j,k); addedge(k,j); } dfs1(1,-1);//预处理,计算每个点最多花费的时间 //memset(dp,0,sizeof(dp)); for(i=1;i<=n;i++) for(j=0;j<=T;j++) { dp[i][j][0]=dp[i][j][1]=0; } for(i=1;i<=n;i++) dp[i][0][1]=val[i]; dfs2(1,-1); int ans=0; for(i=0;i<=num[1];i++) { ans=max(ans,dp[1][i][0]); ans=max(ans,dp[1][i][1]); } printf("%d\n",ans); } return 0; }