zoukankan      html  css  js  c++  java
  • 初识树形动规

    这几天又感受到了被游戏(TreeDP)支配的恐惧,先写写初步对树形DP的认识吧。

    首先,根据树形动规这个名字,就能知道这类题目是属于动态规划的范畴的,但是它们又有和普通动规完全不同的地方:在树上完成。

    普通的动规一般都是在图上或者线性的,在图上,我们有向前和向后两个方向;线性动规中,我们有顺推和逆推两种思路;同样的,树上动规也对应的有两个方向:即从叶到根和从根到叶。和倒推和逆推类似。当然,具体问题具体分析,在不同的题目中,这两种方法有着不同的灵活的应用,没有高低之分。

    之所以出现了这一类问题,是因为树本身就是一种非常毒瘤精彩的结构,它和图不同,自身就配备着“子树”,正好与DP中的“子问题”可以搭上边,都是从整体化成细小问题来解决的。

    在树形动规中,也有几个比较难以实现的地方。

    首先就是递归。

    在树形动规中,平时繁杂的几重for循环变为了一层又一层看不透的递归,使用不熟练的话可能会有困难。

    然后是细节多。

    各种层出不穷的细节需要你用各种特判才能完美实现,如果脑子不清醒做起来就会非常恶心.

    此外,就是所有动规题目共有的恶心之处,推动态转移方程式,这个没有捷径可走,只有通过多练习,找到手感之后才能略微提升自己刷DP题目的能力。

    题目

    洛谷P2015 二叉苹果树

    大致题意:在一棵二叉树上求一条包含根节点且只包含q条边的道路并且使这条道路经过的边权总和最大。特殊条件:每个结点的儿子数量只会是0或2.

    为什么一定包含根节点呢?因为这是一棵倒着长的数(雾)如果挖掉根节点,所有边都会消失。

    所以考虑分成更小的子问题,从叶子结点向上扫,用(dp[i][j])表示编号是(i)的结点为根时的字数在保留(j)条边时最大的权值和,然后扫一遍每个点都能够得到更新。

    更新的过程中需要考虑。比如当前已经得到了(dp[i][j])的值,往上推的时候就是(dp[父节点编号][j+1]+连接的权值)作为一个选择可能,与另外一个子树的相同情况打擂就ok。

    还需要注意的一个坑点是,在选择中,不一定要选择根节点左子树或右子树的全部,可以两边各自选取一部分,只要边数和符合条件就可以更新。

    (code:)

    #include<bits/stdc++.h>
    #define max(a,b) (a>b?a:b)
    using namespace std;
    struct node{int to,w;};
    int n,q;
    vector<node> g[110];
    int dp[110][110];
    void dfs(int u,int fa)
    {
    	int ls_id=-1,rs_id=-1;
    	for(int i=0;i<g[u].size();i++)
    	{
    		if(g[u][i].to==fa)continue;
    		if(ls_id!=-1)rs_id=i;
    		else ls_id=i;
    		dfs(g[u][i].to,u);
    	}
    	dp[u][0]=0;
    	for(int i=1;i<=q;i++)
    	{	
    		if(ls_id!=-1)dp[u][i]=max(dp[u][i],dp[g[u][ls_id].to][i-1]+g[u][ls_id].w);
    		if(rs_id!=-1)dp[u][i]=max(dp[u][i],dp[g[u][rs_id].to][i-1]+g[u][rs_id].w);
    		if(rs_id!=-1&&ls_id!=-1)
    			for(int j=1;j<i;j++)
    				dp[u][i]=max(dp[u][i],dp[g[u][ls_id].to][j-1]+dp[g[u][rs_id].to][i-j-1]+g[u][ls_id].w+g[u][rs_id].w);
    	}
    		
    }
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<n;i++)
    	{
    		int a,b,c;
    		scanf("%d%d%d",&a,&b,&c);
    		g[a].push_back((node){b,c});
    		g[b].push_back((node){a,c});
    	}
    	dfs(1,0);
    	cout<<dp[1][q]<<endl;
    	return 0;
    } 
    

    题目待更新...

  • 相关阅读:
    springboot:springboot初识(相关概念、新建项目)
    ssm项目无法加载静态资源
    js:初识(相关概念、js代码的书写位置、注释方式、输入输出语句)
    lucene:索引维护(删除、更新、查询)
    数据库连接池:Durid(执行流程、工具类)
    redis:HyperLogLog&发布订阅(HyperLogLog的概念和命令、redis的发布订阅)
    redis:zset(赋值、取值、删除、修改分数)
    css:css3新特性(过渡)
    css:css3新特性(盒子模型的box-sizing属性、图片模糊处理、calc函数)
    css:css3新特性(属性选择器、结构伪类选择器)
  • 原文地址:https://www.cnblogs.com/moyujiang/p/12217201.html
Copyright © 2011-2022 走看看