【DP计划】11.7——[CF]CF815C(树形依赖背包)MEDIUM

本文介绍了一种基于树形结构的购物折扣策略优化算法,通过定义f[i][j]和g[i][j]数组,利用动态规划思想进行背包处理,以求解在有限预算下,如何购买商品以享受最大折扣的问题。
题目传送门
附图:

在这里插入图片描述

题目大意:
你要去商店买东西,每个东西有一个价格 c i c_i ci和一个折扣 d i d_i di,你可以享受到物品 d i d_i di的折扣当且仅当你之前买了物品 x i x_i xi,并且享受到了物品 x i x_i xi的折扣。保证除第一个点之外,每个点都只有一个 x i x_i xi(第一个点没有 x i x_i xi)。你有 m m m元钱,求你最多能买多少东西。

由题意得,折扣关系是一棵树,享受这个点的折扣的条件是买它的父亲,并且父亲也享受折扣,因此也要买父亲的父亲,父亲的父亲也享受折扣……因此,享受一个点折扣最终的条件就是这个点的祖先全部都买。
这样也就形成了一个依赖关系,我们定义数组 f [ i ] [ j ] f[i][j] f[i][j]表示点 i i i,它的子树(包括自己)买了 j j j个点,且购买点 i i i时享受到了折扣的最小费用; g [ i ] [ j ] g[i][j] g[i][j]表示点 i i i,它的子树里买了 j j j个点,并且所有的点都不享受折扣的最小费用。那么进行背包处理,即可得出转移的方程式:
g[x][0]=0;f[x][1]=v[x]-dc[x];g[x][1]=v[x];siz[x]=1;
for(i=siz[x];i>=0;i--)
  for(j=1;j<=siz[to];j++)
    g[x][i+j]=min(g[x][i+j],g[x][i]+g[to][j]);
for(i=siz[x];i>=1;i--)
  for(j=1;j<=siz[to];j++)
    f[x][i+j]=min(f[x][i+j],f[x][i]+min(f[to][j],g[to][j])); 
这个方程式可以这样理解,对于上面那个 g g g数组,相当于就是一个分组背包,每个子树可以看成一个组,组里只能选一种情况。
f f f数组也是同理,只是由于 i i i点必须享受到折扣,所以 i i i点必选,那么枚举 s i z e size size的时候只枚举到 1 1 1即可。
这里还有一个操作,就是 s i z [ x ] siz[x] siz[x]先处理后加,这样做能极大地优化时间复杂度。
#include<bits/stdc++.h>
#define MAXN 5005
#define ll long long
using namespace std;
ll read(){
	char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
	while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,m,ans,flag,v[MAXN],dc[MAXN],f[MAXN][MAXN],g[MAXN][MAXN];
ll cnt,head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],siz[MAXN];
void add(ll x,ll y){
	go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;
}
void dfs(ll x){
	register int i,j,k;
	g[x][0]=0;f[x][1]=v[x]-dc[x];g[x][1]=v[x];siz[x]=1;
	for(k=head[x];k!=-1;k=nxt[k]){
		ll to=go[k];dfs(to);
		for(i=siz[x];i>=0;i--)
		 for(j=1;j<=siz[to];j++)
		  g[x][i+j]=min(g[x][i+j],g[x][i]+g[to][j]);
		for(i=siz[x];i>=1;i--)
		 for(j=1;j<=siz[to];j++)
		  f[x][i+j]=min(f[x][i+j],f[x][i]+min(f[to][j],g[to][j])); 
		siz[x]+=siz[to];
	}
}
int main()
{
	n=read();m=read();register int i;
	memset(head,-1,sizeof(head));
	memset(f,127,sizeof(f));
	memset(g,127,sizeof(g));
	for(i=1;i<=n;i++){
		v[i]=read();dc[i]=read();
		if(i>1){ll x=read();add(x,i);}
	}
	dfs(1);
	for(i=n;i;i--)
	 if(f[1][i]<=m||g[1][i]<=m){flag=1;printf("%d",i);break;}
	if(!flag) puts("0");
	return 0;
}
`dmidecode` 是一个在 Linux 系统中用于获取系统硬件信息的工具,它可以输出如制造商、产品名称、版本、序列号等信息。以下是对这些信息的解析以及它们与 OpenStack Nova 的关联: ### 制造商(Manufacturer) 制造商信息表明了硬件设备的生产厂家。在 `dmidecode` 输出中,通常可以在系统信息部分找到制造商信息。例如: ```plaintext System Information Manufacturer: Dell Inc. ``` 在 OpenStack Nova 中,硬件制造商信息可能对资源调度和兼容性检查有一定作用。不同制造商的硬件可能在性能、驱动支持等方面存在差异,Nova 在调度虚拟机时可能会考虑这些因素。 ### 产品名称(Product Name) 产品名称描述了具体的硬件产品型号。示例如下: ```plaintext System Information Product Name: PowerEdge R740xd ``` OpenStack Nova 可以利用产品名称来了解硬件的具体规格和性能特点,从而更合理地分配虚拟机资源。例如,某些产品型号可能具有更高的 CPU 性能或更大的内存容量,Nova 可以根据这些信息将对资源要求较高的虚拟机调度到合适的物理主机上。 ### 版本(Version) 版本信息可能指硬件的版本号或 BIOS 版本等。例如: ```plaintext BIOS Information Vendor: Dell Inc. Version: 2.13.0 ``` BIOS 版本等信息对于系统的稳定性和兼容性至关重要。在 OpenStack 环境中,如果硬件版本过低,可能会存在安全漏洞或不支持某些新的功能特性。因此,了解硬件版本信息有助于进行系统维护和升级。 ### 序列号(Serial Number) 序列号是每个硬件设备的唯一标识符。示例如下: ```plaintext System Information Serial Number: 2G45678 ``` 序列号可以用于硬件设备的管理和跟踪。在 OpenStack Nova 中,序列号可以用于识别物理主机,方便进行资产管理和故障排查。例如,当某个物理主机出现故障时,可以通过序列号快速定位到具体的设备。 ### 示例命令及输出 要获取上述信息,可以使用以下命令: ```bash dmidecode -t system ``` 该命令将输出系统信息,包括制造商、产品名称、版本和序列号等。 ### 与 OpenStack Nova 的关联 OpenStack Nova 是 OpenStack 云计算平台的计算服务组件,负责虚拟机的创建、调度和管理。硬件的制造商、产品名称、版本和序列号等信息对于 Nova 的资源管理和调度决策具有重要意义。通过了解这些信息,Nova 可以更好地匹配虚拟机的资源需求和物理主机的硬件特性,提高资源利用率和系统性能。 ### 代码示例 以下是一个简单的 Python 脚本,用于解析 `dmidecode` 输出并提取相关信息: ```python import subprocess def get_dmidecode_info(): result = subprocess.run(['dmidecode', '-t', 'system'], capture_output=True, text=True) output = result.stdout lines = output.split('\n') info = {} for line in lines: if ':' in line: key, value = line.split(':', 1) key = key.strip() value = value.strip() if key in ['Manufacturer', 'Product Name', 'Version', 'Serial Number']: info[key] = value return info dmidecode_info = get_dmidecode_info() print(dmidecode_info) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值