树形DP初探
来源:互联网 发布:350淘宝装修安全吗 编辑:程序博客网 时间:2024/05/30 04:11
声明:
一下学习内容摘自–树型动态规划 JSOI2010冬令营
关于DP:
DP的赘述就不多说了,强调两点:
最优子结构:一个最优化策略的子策略总是最优的。
无后效性:当前决策与过去状态无关。
再加上树:
有n个点,n-1条边的无向图,任意两顶点间可达
无向图中任意两个点间有且只有一条路
一个点至多有一个前趋,但可以有多个后继
无向图中没有环
引例、问题描述
给定一棵树,树的每个结点有一个权值,要求从中选出一些不相邻的点,使选出的结点权值和最大。
引例、问题分析
首先要给这棵树选一个根,明确了父子关系才有动规的顺序。本题没有特殊要求,只要任意选择一个点作根就可以了。
引例、确定状态
用f[i][0]表示不选i时,以i为根子树的最大权值;用f[i][1]表示选择i时,以i为根子树的最大值。
引例、状态转移
f[i][0]=sum(max(f[j][0],f[j][1]))
f[i][1]=sum(f[j][0])+v[i]
引例、两种实现方式
记忆划搜索:易于实现,但可能会爆栈
拓扑排序+动规:实现起来比较麻烦
引例、两种实现方式
实现方式的选择因题而异,对于本题,首先要保证程序不会出错。但一般来说,在保证正确的前提下,记忆划搜索更加易于实现,且在对于复杂的题目,记忆划搜索更加直观,便于思考。
引例、小结
动态规划都需要一个决策序列,而许多题目的树是无序给出的,要做动规,我们要选择好合适的根。
对于大多数树型动态规划问题,都是用一棵子树的根结点编号来作为代表这棵子树的第一维状态,然后再根据需要加维。
因为树的特殊结构,任何两个点只有唯一通路,所以很容易满足无后效性。假如本题给定的是图而不是树,那么显然就无法用动规解决了。
对于此题代码…觉得贴出来实在是亵渎原作者的原意,个人也觉得不应该看代码,而进行自己打,无非就是一个递推套了个DFS嘛。
赠送案例:
输入:61 2 1 2 4 11 21 31 62 55 4输出:8
感觉到了原文中的第二题深深的恶意,所以还是先写第三题吧
例三 问题描述(ural1018)
有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点)。这棵树共有 N(1<=N<=100) 个结点(叶子点或者树枝分叉点),编号为 1-N, 树根编号一定是 1。
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。 给定需要保留的树枝数量P,求出最多能留住多少苹果。
例三、问题分析
这题的权值在边上,这在思考时有些别扭,其实只要把边的权值转移到儿子结点上,问题性质不变。
//其实就是按照树+DP的特性,父节点与子节点的关系联系于状态转移
这样状态就应该容易想到了,dp[i][j]表示以i结点为根的子树保留j个结点所得的最大值。因为根结点没有权值,所以我们要保留p+1个点。
/*******/
//定义:
//dp[i][j]表示以i结点为根的子树保留j个结点所得的最大值。
/*******/
//状态转移:
//对于根节点为 i 保留 j 个节点的子树,枚举左子树保留的节点个数k,
//dp[i][j]=max{dp[i_left][k]+dp[i_right][j-k-1],k [0, j-1] };
//对于边界 dp[i][0] = 0; dp[i][1] = value[i];
具体代码实现:
先按照边输入,然后预处理每个节点为根时当前子树的节点数量。
然后记忆化搜索,对当前的节点 i ,当前保留的节点数 j ,
先判断掉节点是否超过当前节点的范围。
然后对它的子节点枚举子节点数进行DFS。
const int N=1e2+10;const int M=2e2+10;struct Edge{ int val; int v; int next;}edge[M];int head[N],tol,n,p;int cnt[N];int w[N];void init(){ tol=0; memset(head,-1,sizeof(head));}void add(int u,int v,int val){ edge[tol].val=val; edge[tol].v=v; edge[tol].next=head[u]; head[u]=tol++;}bool vis[N];int dp[N][N];int DFS(int u,int num,int pre){ int v,temp; if(num>cnt[u]) return 0; if(dp[u][num]!=-1) return dp[u][num]; int node1=-1,node2=-1; for(int i=head[u];~i;i=edge[i].next){ v = edge[i].v; if(v==pre) continue; if(node1 == -1) node1 = v; else node2 = v; } temp = 0; for(int i=0;i<num;i++){ temp = max(DFS(node1,i,u)+DFS(node2,num-1-i,u),temp); } dp[u][num] = temp + w[u]; return dp[u][num];}void solve(){ memset(vis,false,sizeof(vis)); memset(dp,-1,sizeof(dp)); for(int i=1;i<=n;i++){ dp[i][0] = 0; dp[i][1] = w[i]; } vis[1] = true; printf("%d\n",DFS(1,p+1,1));}int DFS1(int u){ int v,res; if(cnt[u]) return cnt[u]; res = 1; for(int i=head[u];~i;i=edge[i].next){ v = edge[i].v; if(vis[v]) continue; vis[v] = true; w[v] = edge[i].val; res += DFS1(v); } cnt[u] = res; return res;}int main(){ int u,v,val; scanf("%d%d",&n,&p); init(); for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&val); add(u,v,val); add(v,u,val); } memset(vis,false,sizeof(vis)); vis[1] = true; memset(cnt,0,sizeof(cnt)); DFS1(1); solve(); return 0;}
- 树形DP初探
- 树形动态规划(树形DP)入门问题—初探 & 训练
- 树形dp
- 树形DP
- 树形dp
- 树形DP
- 树形dp
- 树形DP
- 树形DP
- 树形DP
- 树形DP
- 树形dp
- 树形dp
- 树形dp
- 树形dp
- 树形DP
- 树形DP
- 树形DP
- cocos2d-x 3.13 Label 的使用
- 2017-05-10 idea的git提交push出现missing changeid的问题
- 51nod 1163 最高的奖励(贪心+堆)
- 有关个人租房的一些细节
- Problem D: 驾驶员与汽车
- 树形DP初探
- POJ测试数据合集
- JAVA知识点总结-13IO
- IT痴汉的工作现状52-神队友与猪队友
- angularJs从零开始(一)使用yeoman搭建一个企业级的angularjs项目
- Android Studio主题设置、颜色背景配置
- PAT-A-1071. Speech Patterns (25)
- 蓝桥杯java第八届B组:取位数
- python pandas 怎么替换某列的一个值?