array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 树形DP总结 - 爱码网
ForeverCC

Part 0 前言

刚学完了树形DP,是时候来总结一下了!?

Part 1 基础部分

树形DP就是在树上的DP。

这里先放一下遍历树的模板:

void dfs(int u,int fa){
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)continue;
		dfs(v);
	}
}

DP的操作就放在dfs操作的后面,再加上初值和特判,就变成了这样:

void dfs(int u,int fa){
	|初值定义| 
	if(|特判条件|)|特判操作| 
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)continue;
		dfs(v);
		|DP操作|
	}
}

Part 2 练习部分

P1352 没有上司的舞会

难度:★★☆☆☆

题目大意:给你一棵带点权的树,每次只能取父节点的权或子节点的权,求最大的点权和为多少。

分析:因为能不能区由子节点决定,那么可以设 \(f_{i ,0/1}\) 为节点 \(i\) 取(1)和不取(0)的最大权值和。转移方程就为 \(f_{i,1} = f_{j,0} + a_i\)\(f_{i,0} = max(f_{j,0} , f_{j,1})+ a_i\)\(j\)\(i\) 的子节点,\(a_i\) 为节点 \(i\) 的权值)。答案就是 \(max(f_{root,0},f_{root,1})\)

代码实现

#include<bits/stdc++.h>
#define maxn 6005
using namespace std;
int n,head[maxn],nex[maxn],to[maxn],f[maxn][2],num[maxn],edgenum;
bool root[maxn];
void addedge(int u,int v){ 
	nex[++edgenum]=head[u];
	to[edgenum]=v;
	head[u]=edgenum;
	root[v]=1;
}
void dp(int u){
	f[u][0]=0;//定义初值 
	f[u][1]=num[u];
	for(int i=head[u];i;i=nex[i]){
		int v=to[i];
		dp(v);
		f[u][0]+=max(f[v][0],f[v][1]);//DP操作 
		f[u][1]+=f[v][0];
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&num[i]);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(v,u);
	}
	for(int i=1;i<=n;i++)
		if(!root[i]){//是否为根节点 
			dp(i);
			printf("%d\n",max(f[i][0],f[i][1]));//输出答案 
			break;
		}
	return 0;
}

P1122 最大子树和

难度:★☆☆☆☆

题目大意:给你一棵带点权的树,让你求最大的子树权值和。

分析:因为要求最大子树和,所以设 \(f_u\) 为以节点 \(u\) 为根的最大子树和。\(f_u = max(0,f_v)+a_u\)\(v\)\(u\) 的子节点,\(a_u\) 为节点 \(u\) 的权值)。答案就是 \(f_u\) 的最大值。

代码实现

#include<bits/stdc++.h>
using namespace std;
struct edge {
	int next,to;
} e[100000];
int n,a[100000],head[100000],cnt,f[100000],ans;
void add(int x,int y) {
	e[++cnt].next=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void dfs(int u,int fa) {
	f[u]=a[u];
	for(int i=head[u]; i; i=e[i].next) {
		int v=e[i].to;
		if(v!=fa) { 
			dfs(v,u);
			f[u]+=max(0,f[v]);//相加 
		}
	}
	ans=max(ans,f[u]);//记录最大值 
}
int main() {
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	for(int i=1;i<n; i++) {
		int x,y; 
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	printf("%d",ans);
	return 0;
}

P2015 二叉苹果树

难度:★★★☆☆

题目大意:给你一棵带点权的二叉树,求一颗有 \(Q\) 条边的子树的最大点权和。

分析:通过样例可以看出这是一道树上的 01背包 题目,通过类比思想把状态转化为 \(f_{x,i}\) 表示当前节点 \(x\) 的子树上保留 \(i\) 条边的最大点权和。转移就变成了这样:

for(int j=q;j>=1;j--)
	for(int k=j-1;k>=0;k--)
		f[x][j]=max(f[x][j],len[i]+f[v][k]+f[x][j-k-1]);

搜索,每一个点找出以它为根节点的1~q的最大利益,除叶节点外。因为连接父节点和子节点还有一条边,所以父节点自己留的可用边-1。

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,q;
int f[1005][1005],edgenum,len[1009],head[1009],to[1005],nxt[1005];
void addedge(int u,int v,int l){
	nxt[++edgenum]=head[u];
	to[edgenum]=v;
	len[edgenum]=l;
	head[u]=edgenum;
}
void dfs(int x,int fa){
	for(int i=head[x];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)continue;
		used[v]=1;
		dfs(v,u);
		for(int j=q;j>=1;j--)//01背包要倒着做 
			for(int k=j-1;k>=0;k--)
				f[x][j]=max(f[x][j],len[i]+f[v][k]+f[x][j-k-1]);
	}
}

int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<n;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		addedge(x,y,z);//双向建边 
		addedge(y,x,z);
	}
	dfs(1,0);
	printf("%d",f[1][q]);
	return 0;
}

P2014 [CTSC1997]选课

难度:★★★☆☆

题目大意:给你一棵带点权的树,求一棵有 \(M\) 个点的子树的最大权值和。

分析:跟上面一题差不多,只是变成了单向边。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=305,M=2001;
int f[N][N],edgenum,n,m;
int head[M],nxt[M],to[M];
void addedge(int u,int v){
    nxt[++edgenum]=head[u];
    to[edgenum]=v;
    head[u]=edgenum;
}
void dp(int x){
    for(int e=head[x];e;e=nxt[e]){
        int v=to[e];
        dp(v);
        for(int i=m+1;i>1;i--)
            for(int j=i-1;j>=1;j--)
                f[x][i]=max(f[x][i],f[x][i-j]+f[v][j]);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        f[i][1]=b;
        addedge(a,i);
    }
    dp(0);
    printf("%d\n",f[0][m+1]);
    return 0;
}

P1273 有线电视网

难度:★★★★☆

题目大意:选出一个节点数量最多的叶子节点的集合,使这些点到根节点的路径上的权值和减去集合中所有的点的权值大于等于零。

分析:设 \(f_{i,j}\) 表示在 \(i\) 的子树中,选择 \(j\) 个叶子节点,点权-边权的最大值。dp[u][j]=max(dp[u][j],dp[u][j-i]+dp[v][i]-edge[e].w);,最后找第一个大于等于零的 \(f_{1,i}\) 即为答案。

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,m,head[3010],edgenum,val[3010],dp[3010][3010];
struct node {
	int to,next,w;
} edge[10000];
void adde(int u,int v,int w) {
	edge[++edgenum].to=v;
	edge[edgenum].next=head[u];
	edge[edgenum].w=w;
	head[u]=edgenum;
}
int dfs(int u) {
	if(u>n-m) {
		dp[u][1]=val[u];
		return 1;
	}
	int sum=0,t;
	for(int e=head[u]; e; e=edge[e].next) {
		int v=edge[e].to;
		t=dfs(v);
		sum+=t;
		for(int j=sum; j>0; j--)
			for(int i=1; i<=t; i++)
				if(j-i>=0) dp[u][j]=max(dp[u][j],dp[u][j-i]+dp[v][i]-edge[e].w);
	}
	return sum;
}
int main() {
	memset(dp,~0x3f,sizeof(dp));
	scanf("%d%d",&n,&m);
	for(int u=1; u<=n-m; u++) {
		int size,v,w;
		scanf("%d",&size);
		for(int j=1; j<=size; j++) {
			scanf("%d%d",&v,&w);
			adde(u,v,w);
		}
	}
	for(int i=n-m+1; i<=n; i++)
		scanf("%d",&val[i]);
	for (int i=1; i<=n; i++)
		dp[i][0]=0;
	dfs(1);
	for (int i=m; i>=1; i--)
		if (dp[1][i]>=0) {
			printf("%d",i);
			break;
		}
	return 0;
}

分类:

技术点:

c++

相关文章: