树形DP入门

来源:互联网 发布:蜡笔同步云端数据删除 编辑:程序博客网 时间:2024/05/17 03:06

(因为上次比赛sb地把一道树形dp当费用流做了,受了点刺激,用一天时间稍微搞一下树形DP,今后再好好搞一下)

基于背包原理的树形DP

poj 1947 Rebuilding Roads

题意:给你一棵树,让你求最少剪掉多少条边可以剪出一棵点数为m的子树.

解法:dp[i][j]表示i节点得到j个节点的子树至少要剪多少边,对于每个节点a和它的孩子b,如果剪掉b,则dp(s)[a][j]=dp(s-1)[a][j], 如果保留<a,b>dp(s)[a][j]=min{dp(s-1)[a][j - k] + dp[b][k]}.初始条件为dp[a][1] = 0;

为了不产生后效性,需要由大到小枚举j的值。ans=min{dp[i][m] + 1,dp[root][m]}

void dfs(int a) {dp[a][1] = 0;for (int i = E[a]; i != -1; i = buf[i].ne) {int b = buf[i].be;dfs(b);num[a]+=num[b];for (int j =Math.min(m,num[a]); j > 0; j--) {dp[a][j]++;for (int k = 1; k <= j&&k<=num[b]; k++)dp[a][j] = Math.min(dp[a][j], dp[a][j - k] + dp[b][k]);}}}

poj 1155 TELE 

题意:某电台要广播一场比赛,该电台网络是由N个网点组成的一棵树,其中M个点为客户端,其余点为转发站。客户端i愿支付的钱为pay[i],每条边需要的花费固定,问电台在保证不亏损的情况下,最多能使多少个客户端接收到信息?广播台所在的节点编号为1。

解法:dp[a][j] = Math.max(dp[a][j], dp[a][j - k] + dp[b][k]- buf[i].v);节点a给j个节点输送信号能赚多少钱k <= j && k <= num[b],b是a的孩子节点),初始化是dp[客户][1]=pay[i];
满足dp[1][i]>0最大的i即是答案。
HDU 1561 the more the better
题意:有N座城堡,每座城堡都有一定的宝物,允许攻克M个城堡并获得里面的宝物。但有些城堡必须先攻克其他某一个特定的城堡才能攻克,问攻克M个城堡所获得的最多宝物的数量。
解法:如果必须攻克a才能攻克b则连边<a,b>否则连边<0,b>;
dp[a][j] = Math.max(dp[a][j], dp[a][k] + dp[b][j - k]);dp[a][j]代表从i开始攻克j的城堡的最大获利,初始化时dp[i][1]=i城堡内的宝贝数,dp[0][1]=0;dp[i][0]=0;ans=Max{dp[i][m],dp[0][m+1]};
------------------------------------------------------------------------------------------------------

Poj 2486 Apple Tree

题意:苹果树上有n个节点,每个节点数上有若干个苹果,问最多走m步后至多能吃多少个苹果。

解法:每个在路径上的节点有两种形态:一是以此节点为起点到达某点后不再返回,而是从此节点出发后返回再从其它孩子继续走。因此定义dp[a][j][0]为a节点第一种情况下走j步的最大获利dp[a][j][1]为a节点在第二种情况下走j步的最大获利。转移方程为:

dp[a][j][0]=Math.max(dp[a][j][0],dp[a][k][1]+dp[b][j-k-1][0]);dp[a][j][0]=Math.max(dp[a][j][0],dp[a][k][0]+dp[b][j-k-2][1]);dp[a][j][1]=Math.max(dp[a][j][1],dp[a][k][1]+dp[b][j-k-2][1])

初始化dp[i][0][1]=dp[i][0][0]=i节点的苹果数。ans=Max{0,dp[1][i][0]};


Poj 1655 balancing Act/poj 3107 Godfather

题意:一个节点的平衡因子定义为:删到此节点后形成的节点数最多的子树。求一棵树中平衡因子最大的节点。

解法,定义num[i]为i节点为根的子树的节点数max[i]为i节点孩子节点数的最大值,一个节点的平衡因子=Math.max(n-num[i], max[i]);


poj 1985 Cow Marathon

题意:求一棵树上的最长路径。

解法:first和second为路径最长和第二长的路径,每次dfs返回first,加边权后更新父节点的first和second

ans= Math.max(ans, first + second);


Sgu149&&HDU2196 Computer

题意:求树上每点能到达的最远距离。上题的加强版,经典的两遍dfs。

解法:一个点的最远路径或者是向下一直走或者是先走到父节点然后再从父节点开始走一条较长的路径。
     对于第一种情况由底向上更新求出每点的最长路径和次长路径即可;对于第二种情况,如果当前节点是父节点最长路径上的点,那么向上的最长路径=边权+父节点的次长路径,否则最长路径=边权+父节点的最长路径。得到向上的最长路径后更新最长路径和次长路径(如果更新了最长路径,需要更新一下原来向下最大孩子的状态,使它不是父节点最长路径上的点),从上向下dfs更新一下即可。

int dfs(int a) {int temp, f = 0;for (int i = E[a]; i != -1; i = buf[i].ne) {int b = buf[i].be;temp = dfs(b) + buf[i].v;if (temp > first[a]) {second[a] = first[a];first[a] = temp;f = b;}else if(temp>second[a])second[a]=temp;}fid[a] = f;isf[f] = true;return first[a];}void work(int p, int a, int v) {int temp = -1;if (isf[a])temp = second[p] + v;elsetemp = first[p] + v;if (temp > first[a]) {second[a] = first[a];first[a] = temp;isf[fid[a]] = false;} else if (temp > second[a])second[a] = temp;for (int i = E[a]; i != -1; i = buf[i].ne) {int b = buf[i].be;work(a, b, buf[i].v);}}