Codeforces Round #595 (Div. 3) F - Maximum Weight Subset(贪心)

题意

n(n<=200)个节点的树,树上距离小于等于k(k<=200)的两个点不能同时选,

每个点有个点权ai(1<=ai<=1e5),求最大权重和

题解1

Claris的做法,先bfs一遍,然后倒序扫bfs序列,

每次贪心选这个值,并把距离≤k的点都减去这个点的权值,

代表选这个值给别的点带来的不能选的影响,

如果扫到一个点为正,此时代表把残值加上,相当于反选

倒序是因为,只有所有儿子都确定了,父亲才能被确定,是否应该反选

看似是O(n^2)的

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,k,x,y,z,a[N],head[N],cnt;
bool vis[N];
int q[N],h,t,ans;
struct node{int v,nex;}e[N*2];
void add(int u,int v){e[++cnt]=node{v,head[u]};head[u]=cnt;}
void ext(int u)
{
	if(vis[u])return;
	vis[u]=1;
	q[++t]=u;
}
void dfs(int u,int fa,int d)
{
	if(d>k)return;
	a[u]-=z;
	for(int i=head[u];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u,d+1);
	}
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]);
	for(int i=1;i<n;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	h=1;t=0;
	ext(1);
	while(h<=t)
	{
		x=q[h++];
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			ext(v);
		}
	}
	for(int i=n;i;i--)
	{
		x=q[i];
		if(a[x]<=0)continue;
		z=a[x];
		ans+=z;
		dfs(x,0,0);
	}
	printf("%d\n",ans);
	return 0;
}

题解2

考虑O(n^3)的dp,

dp[i][j]表示对于i来说,只选i以下深度>=j的点的最大代价和,

子树合并的时候,考虑u和v最浅的距离必须>k,u才能选v

判断u选不选的时候,就是dp[u][0]从dp[u][w],w>k处转移过来,

dp数组开得足够大,就不用加判断n和k大小的限制了

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=205;
int n,u,v,k,a[N],dp[N][N],tmp[N],ans;
//dp[i][j]:i为根 选的点只来自i以下深度>=j的点 的最大代价和 
vector<int>e[N];
void dfs(int u,int fa){
	for(auto &v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		for(int i=0;i<N;++i){
			tmp[i]=dp[u][i];
		}
		for(int i=0;i<N;++i){
			for(int j=max(0,k-i);j<N;++j){//i+j+1>k u内选v 
				int w=min(i,j+1);
				dp[u][w]=max(dp[u][w],tmp[i]+dp[v][j]);
			}
		}
		for(int i=1;i<N;++i){
			dp[u][i]=max(dp[u][i],dp[v][i-1]);
		}
	}
	for(int i=k+1;i<N;++i){//选u 
		dp[u][0]=max(dp[u][0],dp[u][i]+a[u]);
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<n;++i){
		scanf("%d%d",&u,&v);
		e[u].pb(v);e[v].pb(u);
	}
	dfs(1,-1);
	for(int i=0;i<N;++i){
		ans=max(ans,dp[1][i]);
	} 
	printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值