【Ybtoj 第22章例2】结点覆盖【树形DP】

这篇博客介绍了一种动态规划方法来解决树形结构的覆盖问题。通过转移方程,博主详细解释了如何计算节点被父亲、孩子或者自身覆盖的情况,并提供了相应的代码实现。在代码中,使用深度优先搜索遍历树的结构,计算每个节点的最优状态。最终,通过比较节点的两种状态来确定全局的最小覆盖方案。

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

在这里插入图片描述


解题思路

个节点被覆盖只有三种可能:设fx,0/1/2f_{x,0/1/2}fx,0/1/2 分别表示被父亲/孩子/自己/覆盖,可以得到转移方程:

  • fx,0=∑fsonx,0f_{x,0}=∑f_{son_x,0}fx,0=fsonx,0
    (因为他被自己的父亲覆盖,所以它本身及它的子节点都不能选,若选了儿子,就会被儿子再覆盖,故子节点只有000的状态)

  • fx,1=min(fsonx,1−min(fsonx,1,fsonx,0))+∑min(fsonx,0,fsonx,1)f_{x,1}=min(f_{son_x,1}-min(f_{son_x,1},f_{son_x,0}))+∑min(f_{son_x,0},f_{son_x,1})fx,1=min(fsonx,1min(fsonx,1,fsonx,0))+min(fsonx,0,fsonx,1)
    (因为被儿子覆盖,有一个儿子必选。所以先把所有儿子的最优情况算出来,然后枚举那个要选的儿子,若那个儿子是fy,1f_{y,1}fy,1,就要用fy,2f_{y,2}fy,2替换)

  • fx,2=rx+∑min(fsonx,0,fsonx,1,fsonx,2)f_{x,2}=r_x+∑min(f_{son_x,0},f_{son_x,1},f_{son_x,2})fx,2=rx+min(fsonx,0,fsonx,1,fsonx,2)
    (子节点随便选)

最终答案即为 min(fx,1,fx,2min(f_{x,1},f_{x,2}min(fx,1,fx,2) )(因为根没有父节点)


代码

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;

long long f[2000][3],r[2000],n,x,y,k,m,head[3100];

struct c{
	int x,next;
}a[3100];

void add(int x,int y)
{
	a[++k].x=y;
	a[k].next=head[x];
	head[x]=k;
}

void dfs(int dep,int fa){
	long long d=0;
	f[dep][1]=1e9,f[dep][2]=r[dep];
	for(int i=head[dep];i;i=a[i].next)
	{
		int y=a[i].x;
		if(y==fa)continue;
		dfs(y,dep);
		f[dep][0]+=f[y][1];
		d+=min(f[y][1],f[y][2]);
		f[dep][2]+=min(f[y][2],min(f[y][1],f[y][0]));
	}
	for(int i=head[dep];i;i=a[i].next)
	{
		int y=a[i].x;
		if(y==fa)continue;
		f[dep][1]=min(f[dep][1],d-min(f[y][2],f[y][1])+f[y][2]);
	}
	
}

int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		scanf("%lld%lld",&r[x],&m);
		for(int j=1;j<=m;j++)
		{
			scanf("%lld",&y);
			add(x,y);
			add(y,x);
		}
	}
	dfs(1,0);
	printf("%lld",min(f[1][1],f[1][2]));
	return 0;
		
	
}
/* 
9
2 16 3 1 3 4
1 8000 3 5 6 7
3 6000 1 8
4 8000 0
5 1 1 9
6 900 0
7 100 0
8 2 0
9 20 0
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值