树形DP:k结点子树最大权值和

很基础的一类题目,要用到01背包问题的思想。

ZOJ3201

题解

  • 含有k个结点的权值和最大的子树。
  • dp[i][j]表示以i为根的,含有j个结点的最大权值
  • dp[i][j] = max(dp[i][j],dp[i][j-k] + dp[v][k])。v是i的子结点。类似于分组背包问题,每个结点算一个组,每个组又有很多种选择。

代码

#include <bits/stdc++.h>
using namespace std;
int const N = 100 + 10;
int n,k,dp[N][N],sz[N],ans;
vector<int>G[N];
void dfs(int u,int fa){
	sz[u] = 1;
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(v == fa)	continue;
		dfs(v,u);
		sz[u] += sz[v];
	}
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(v == fa)	continue;
		for(int j=sz[u];j>=1;j--){
			for(int k=1;k<j;k++)
				dp[u][j] = max(dp[u][j],dp[u][j-k] + dp[v][k]);
		}
	}
}
int main(){
	while(~scanf("%d%d",&n,&k)){
		memset(dp,0,sizeof(dp));
		for(int i=0;i<n;i++)
			scanf("%d",&dp[i][1]);   //直接初始化
		for(int i=0;i<n;i++)	G[i].clear();
		for(int i=0;i<n-1;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		dfs(0,-1);
		int ans = 0;
		for(int i=0;i<n;i++)   //遍历每一个点,考虑最大值
			ans = max(ans,dp[i][k]);
		printf("%d\n",ans);
	}
	return 0;
}

ural1018

题解

  • 以1为根保留的q条边的最优值
  • 相当于保留q+1个结点。
  • 和上面一毛一样。只不过这里是边有权值,上边是点有权值

代码

#include <bits/stdc++.h>
using namespace std;
int const N = 100 + 10;
int n,q,dp[N][N],sz[N];   
struct Node
{
	int to,val;	
	Node(){}
	Node(int to,int val):to(to),val(val){}
};
vector<Node>G[N];
void dfs(int u,int fa){
	sz[u] = 1;
	for(int i=0;i<G[u].size();i++){
		Node e = G[u][i];
		if(e.to == fa)	continue;
		dfs(e.to,u);
		sz[u] += sz[e.to];   //sz表示以u为根的数的大小(包括根节点)
	}
	for(int i=0;i<G[u].size();i++){
		Node e = G[u][i];
		if(e.to == fa)	continue;
		for(int j=sz[u];j>1;j--){    //枚举子树的大小,要有根必须有两个结点
			for(int k=1;k<j;k++){ 
				dp[u][j] = max(dp[u][j],dp[u][j-k] + dp[e.to][k] + e.val); 
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i=0;i<n-1;i++){  //二叉树
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		G[u].push_back(Node(v,c));
		G[v].push_back(Node(u,c));
	}
	dfs(1,-1);
	printf("%d\n",dp[1][q+1]);
	return 0;
}

HDU1561

题解

  • 0为根含有m+1个结点的最大子树

代码

#include <bits/stdc++.h>
using namespace std;
int const N = 200 + 10;
int n,m,dp[N][N],sz[N];
vector<int>G[N];
void dfs(int u,int fa){
	sz[u] = 1;
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(v == fa)	continue;	
		dfs(v,u);
		sz[u] += sz[v];
	}
	for(int i=0;i<G[u].size();i++){
		int v = G[u][i];
		if(v == fa)	continue;	
		for(int j=sz[u];j>=1;j--){
			for(int k=1;k<j;k++)
				dp[u][j] = max(dp[u][j],dp[u][j-k] + dp[v][k]);
		}
	}
}
int main(){
	while(~scanf("%d%d",&n,&m) && (n || m)){
		for(int i=0;i<=n;i++)	G[i].clear(),	sz[i] = 0;
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n;i++){
			int a,b;
			scanf("%d%d",&a,&dp[i][1]);
			G[a].push_back(i);
		}
		dfs(0,-1);
		printf("%d\n",dp[0][m+1]);
	}
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值