poj2486解题报告

来源:互联网 发布:户外驴友用哪个软件 编辑:程序博客网 时间:2024/06/05 16:20
     树形DP.
与以往一些分配资源的树形动归相比,这道题的最大不同就是可以往回走。。。即可以从子节点回到根节点。
建树问题:直接dfs在多叉树上做就可以。
设back[x][j]为从标号为x的根节点向下走j步,最终回到i所取得的最大价值;
设pass[x][j]为从标号为x的根节点向下走j步,最终不一定回到i所取得的最大价值。
这里(0<=j<=Max_step)    
初始化:
for(i = 0 ; i <= Max_step ; ++i)
{
            back[x][i] = pass[x][i] = val[x];
}
现在我们考虑为x的子节点son分配资源。下面设从节点son出发向下走的步数为k.(也即给节点son分配的资源)
换一种思考的方式,把son看作一部分,把根节点i剩余的子节点及以其为根的子树看作另一部分。
首先看back[x][j].
我们想要回到根节点i,把这个过程分为两个部分:
step1:从根节点x到达son,从son遍历son的子树再回到son,从son回到son的根节点x;
step2:用剩余的步数遍历根节点x其余的子树,再回到根节点x.
获得的价值:back[son][k] + back[x][j - k - 2]
为什么是j - k - 2呢,因为一共步数为j,从son往返消耗了步数2,在son处消耗了步数k,故还剩(j - 2 - k).
我们得到: back[x][j] = max(back[x][j] , back[son][k] + back[x][j - 2 - k])   (0<=k<=j-2)
接下来看pass[x][j].
既然不一定回到根节点x,那么最后的位置会是哪里呢?
我们刚才说过,要把以当前决策的子节点son为根节点的子树和根节点x的其余子树分开来考虑。
case1:
最后的位置在以子节点son为根节点的树上。
那么:
step1:从根节点遍历其余的子树,最终回到根节点;
step2:从根节点遍历以son为根节点的树,最终不一定回到son.
我们得到:  pass[x][j] = max(pass[x][j] , pass[son][k] + back[x][j - 1 - k])  (0<=k<=j-1)
多出的那个“1”和上面类似,是从根节点x到子节点son的一步。
case2:
最后的位置在其余的子树上。
step1:从根节点遍历以son为根节点的子树,最终回到son,并回到根节点x.
strep2:从根节点出发遍历其余的子树,最终不一定回到son,不知所踪。
我们得到 : pass[x][j] = max(pass[x][j] , back[son][k] + pass[x][j - 2 - k]) (0<=k<=j-2)
状态转移方程大功告成。
还有一个重要问题:j的枚举顺序问题。用类似滚动数组一类东西从Max_size-0枚举。

代码就比较水逼了。

#include<cstdio>#include<cstring>inline int max(int a , int b){return a > b ? a : b;}int pass[101][201] , back[101][201];int val[101];int n,m;int head[101] , next[202] , end[202] , ind;void addedge(int a , int b){int q = ++ind;end[q] = b;next[q] = head[a];head[a] = q;}bool ext[101];int n_son[101] , son[101][101];void build(int x){ext[x] = 1;for(int i = head[x] ; i ; i = next[i]){if(!ext[end[i]]){son[x][++n_son[x]] = end[i];build(end[i]);}}}void dfs(int x){int i,j,k;for(i = 0 ; i <= m ; ++i){pass[x][i] = back[x][i] = val[x];}for(i = 1 ; i <= n_son[x] ; ++i){dfs(son[x][i]);for(j = m ; j >= 0 ; --j){for(k = 0 ; k + 2 <= j ; ++k){back[x][j] = max(back[x][j] , back[son[x][i]][k] + back[x][j - 2 - k]);}for(k = 0 ; k + 2 <= j ; ++k){pass[x][j] = max(pass[x][j] , back[son[x][i]][k] + pass[x][j - 2 - k]);}for(k = 0 ; k + 1 <= j ; ++k){pass[x][j] = max(pass[x][j] , pass[son[x][i]][k] + back[x][j - 1 - k]);}}}}int main(){register int i;while(scanf("%d%d",&n,&m) != EOF){memset(pass , 0 , sizeof(pass));memset(back , 0 , sizeof(back));for(i = 1 ; i <= n ; ++i) scanf("%d",&val[i]);int a,b;ind = 0;memset(head , 0 , sizeof(head));memset(end , 0 , sizeof(end));memset(next , 0 , sizeof(next));for(i = 1 ; i < n ; ++i){scanf("%d%d",&a,&b);addedge(a , b);addedge(b , a);}memset(ext , 0 , sizeof(ext));memset(n_son , 0 , sizeof(n_son));build(1);dfs(1);printf("%d\n",max(pass[1][m] , back[1][m]));}return 0;}


0 0
原创粉丝点击