【NOIP2016提高A组五校联考2】tree

本文探讨了一种在有根树结构中选择节点以最大化特定价值之和的问题,同时需满足路径约束和节点数量限制。通过将问题转化为DFS序列并应用动态规划方法,实现了在O(n^2)时间复杂度内的高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给一棵n 个结点的有根树,结点由1 到n 标号,根结点的标号为1。每个结点上有一个物品,第i 个结点上的物品价值为vi。
你需要从所有结点中选出若干个结点,使得对于任意一个被选中的结点,其到根的路径上所有的点都被选中,并且选中结点的个数不能超过给定的上限lim。在此前提下,你需要最大化选中结点上物品的价值之和。
求这个最大的价值之和。

Input
第一行为两个整数n; lim
接下来n 行,第i 行包含一个整数vi,表示结点i 上物品的价值。
接下来n- 1 行,每行包含两个整数u; v, 描述一条连接u; v 结点的树边。

Output
输出一行答案。

Sample Input
6 4
-5
4
-6
6
9
6
3 2
3 1
2 4
2 5
1 6

Sample Output
2

Data Constraint
对于前20% 的数据,1<=n; lim<=10
对于前60% 的数据,1<=n; lim<=100
对于100% 的数据,1<=n; lim<=3000; |vi| <=10^5 数据有梯度,保证给出的是合法的树。

.
.
.
.
.
分析
把条件转化,设原树有根,如果一个结点不选,那么以其为根的子树都不能选。
显然我们可以把树转化成dfs序列。
设f[i][j]为前i个数中,选了j个数的最大答案。
f[i][j]=max{f[i][j],f[i−1][j−1],f[low[i]][j−1]}
时间复杂度为O(n2)。

.
.
.
.
.
程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

struct edge
{
	int to,from;
}e[100000];

int a[4000],v[4000],n,lim,cnt=0,tot=0,head[100000],dfn[100000],low[100000],f[3005][3005];

void add(int x,int y)
{
	e[++cnt].to=y;e[cnt].from=head[x];head[x]=cnt;
	e[++cnt].to=x;e[cnt].from=head[y];head[y]=cnt;
}

void dfs(int x,int father)
{
    a[++tot]=x;
    dfn[x]=tot;
    for (int i=head[x];i;i=e[i].from)
        if (e[i].to!=father) dfs(e[i].to,x);
    low[dfn[x]]=tot;
}

int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d%d",&n,&lim);
	for (int i=1;i<=n;i++)
		scanf("%d",&v[i]);
	for (int i=1;i<=n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	dfs(1,0);
	memset(f,128,sizeof(f));
	f[0][0]=0;
    for (int i=0;i<=n-1;i++)
        for (int j=0;j<=lim-1;j++)
		{
            if (f[i][j]<-2000000000) continue;
            f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+v[a[i+1]]);
            f[low[i+1]][j]=max(f[low[i+1]][j],f[i][j]);
        }
    int ans=0;
    for (int i=1;i<=n;i++) 
		for (int j=0;j<=lim;j++) 
			ans=max(ans,f[i][j]);
	printf("%d",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值