Acwing 171. 送礼物(双向dfs + 二分)

题目描述:
达达帮翰翰给女生送礼物,翰翰一共准备了 N 个礼物,其中第 i 个礼物的重量是 G[i]。
达达的力气很大,他一次可以搬动重量之和不超过 W 的任意多个物品。
达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表 W 和 N。
以后 N 行,每行一个正整数表示 G[i]。
输出格式
仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。
数据范围
1 ≤ N ≤ 46,
1 ≤ W, G[i] ≤ 231−1
题目链接:送礼物

分析:
一开始会想到用背包问题的DP思维来解,但是我们注意w若是231−1,即使使用一维数组也会超内存,所以是不合理的。
然后考虑dfs去解决,时间复杂度是O(2n),当 n = 46时,246会达到1013级别,这肯定会超时 ,我们就可以考虑分成两半来dfs,223只会达到106,乘以2,224也才达到107,所有就分两次来dfs,第一次dfs前半部分,第二次dfs后半部分,这样就可以解决了。

思路:
1.先dfs前半部分,将他们能拼成的所有数字放入一个HashSet集合(去重),再新建一个List集合存放Set集合的元素并从小到大排序,以便后面的二分计算。

	public static void dfs(int u, long sum) {
   
   
		if (u>=k) {
   
   	//k是n的一半
			set.add((int)sum);
			return;
		}
		dfs(u+1, sum);
		if (sum + g[u] <= w)
			dfs(u+1, sum+g[u]);
	}
	list = new ArrayList<>(set);
	Collections.sort(list);

2.dfs后半部分,用后半部分得到的每一个值与前半部分二分找到的最佳值相加,得到结果,再去更新最大结果即可。

	public static void dfs2(int u, long sum) {
   
   
		if (u>=n
AcWing 10题有依赖的背包问题具有一定特点,有 `N` 个物品和容量为 `V` 的背包,物品间存在依赖关系且构成树状,选择一个物品时必须选择其父节点,需找出使物品总体积不超背包容量且总价值最大的方案并输出最大价值 [^4]。 ### 思路 在树形背包问题里,可将动态规划的状态定义为当前节点以及是否选择了当前节点的物品,状态转移需考虑从当前节点的父节点和子节点转移过来的情况,还有当前节点是否选择物品 [^2]。 ### 代码实现 ```cpp #include<iostream> #include<vector> using namespace std; int f[110][110];//f[x][v]表达选择以x为子树的物品,在容量不超过v时所获得的最大价值 vector<int> g[110]; int v[110],w[110]; int n,m,root; int dfs(int x) { for(int i=v[x];i<=m;i++) f[x][i]=w[x];//点x必须选,所以初始化f[x][v[x] ~ m]= w[x] for(int i=0;i<g[x].size();i++) { int y=g[x][i]; dfs(y); for(int j=m;j>=v[x];j--)//j的范围为v[x]~m, 小于v[x]无法选择以x为子树的物品 { for(int k=0;k<=j-v[x];k++)//分给子树y的空间不能大于j-v[x],不然都无法选根物品x { f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]); } } } } int main() { cin>>n>>m; for(int i=1;i<=n;i++) { int fa; cin>>v[i]>>w[i]>>fa; if(fa==-1) root=i; else g[fa].push_back(i); } dfs(root); cout<<f[root][m]; return 0; } ``` ### 代码解释 - `f[x][v]` 表示选择以 `x` 为子树的物品,在容量不超过 `v` 时所获得的最大价值。 - 初始化 `f[x][i]`(`i` 从 `v[x]` 到 `m`)为 `w[x]`,因为点 `x` 必须选。 - 递归遍历 `x` 的子节点 `y`,在更新 `f[x][j]` 时,需保证 `j` 从 `v[x]` 到 `m`,且分给子树 `y` 的空间 `k` 不大于 `j - v[x]`,以确保能选根物品 `x`。 ### 复杂度分析 - 时间复杂度:由于要遍历每个节点及其子节点,并且在每个节点处进行容量的枚举,时间复杂度为 $O(N * V^2)$,其中 `N` 是物品数量,`V` 是背包容量。 - 空间复杂度:主要用于存储动态规划数组和树的邻接表,空间复杂度为 $O(N * V)$。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Easenyang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值