01背包的变形问题----背包恰好装满

01背包问题分支之背包刚好装满时获取最大价值的物品 01背包问题:指在背包装载重量有限的情况下,挑选不同重量物品。物品身具两种属性,一个是本身价值,一个是本身重量。而01背包指挑选重量相加后小于等于背包承重,且价值最大的东西总和。 而今天做leetcode的每日一题刚好做到01背包相关解法的题目: https://leetcode-cn.com/problems/target-sum/submissions/ 494.目标和:指给你一个数组通过对数组内部的每个数字乘以取相反数或本身然后全部相加等于target值 常规做法是暴力破解,我本身的想法也类似,通过回 阅读详情
AI摘要

本文深入探讨01背包问题的变形——背包恰好装满的情况,通过对比普通01背包,详细解释如何调整算法以确保背包完全填满,并达到最大价值。文章提供了具体的代码实例,阐述了有效状态与无效状态的概念,以及如何处理无效状态,确保最终状态转移矩阵准确反映背包恰好装满的最大价值。

01背包的变形问题----背包恰好装满

在看本文之前建议先看一下我之前发过的01背包详解。
https://blog.youkuaiyun.com/Iseno_V/article/details/100001133

1. 问题的变形

在前面讲到的01背包问题中,现在我们把条件改为 “求当背包恰好装满时候取得的最大价值”。这样的问题其实本质上和原始的01背包问题区别不大,我们只需要做出一点小小的调整。
需要指出的是该问题其实可分为两个问题。
(1)背包能否恰好装满?
(2)如果能恰好装满,恰好装满时候的最大价值是多少?

2. 恰好装满时的处理方法

为了便于理解,这里给出一个用数组实现的01背包代码(空间复杂度未优化),和之前用vector容器实现的原理是一样的,

//01背包--不要求正好装满
#include <iostream>
using namespace std;
#define max(N1,N2) N1>N2?N1:N2
int main()
{
	int N, V;
	while (cin >> V >> N)//这里要求V<1000,N<100,否则后面的数组装不下
	{
		int v[100], w[100];//体积和价值
		int f[1000] = { 0 };//状态转移矩阵,这里将f数组全部初始化为0
		for (int i = 1; i <= N; i++)
		{
			cin >> v[i] >> w[i];//输入原始数据
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = V; j >= v[i]; j--)
			{
				f[j] = max(f[j], f[j - v[i]] + w[i]);
			}
		}
		cout << f[V] << endl;//输出结果
	}
	return 0;
}

如果看不懂上面的代码一定要去本文开头给出的链接去看一看01背包的基本原理。

2.1 有效状态和无效状态

在普通的01背包中,由于不要求恰好装满,我们采用的初始化方式为

int f[1000] = { 0 };//状态转移矩阵,这里将f数组全部初始化为0

这个初始化的方法意思是:对任意的一个状态,把当前的价值初始化为0,代表背包为空时所包含的物体价值为0 。即认为不管背包当前容量,背包是空的那么价值就是0,,没装满背包的状态都是有效的状态,我们称之为有效状态。
当要求恰好装满时,我们就不可以这么做了,因为我们认为背包没恰好装满的话,当前背包的状态无效,即无效状态,只有恰好装满时候才是有效状态。
我们不妨定义当背包的状态为无效状态时,f[i]的值是负无穷,这样我们就可以从状态转移矩阵中区分出有效状态和无效状态了。
先不关心如何计算f,有了这个定义,对于恰好装满的01背包问题,我们只需要判断f[V]中的值是否是负无穷,就知道背包能否装满了。当f[V]中的值不是负无穷,我们接下来需要设计状态转移方程,使f[V]就是背包恰好装满时候的最大价值。

2.2 01背包的有效状态都是根据以前的有效状态推导出来的

首先观察上面的代码,转移方程为

f[j] = max(f[j], f[j - v[i]] + w[i]);

这段代码时经过时间复杂度优化后的代码,在优化前是

for (int i = 1; i <= n; i++)
{
	for (int j = V; j >= 0; j--)
	{
		if (j >= w[i])//如果背包装得下当前的物体
		{
			f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
		}
		else//如果背包装不下当前物体
		{
			f[i][j] = f[i - 1][j];
		}
	}
}

通过前面01背包基本原理的讲解,我们知道,这两段代码是等效的。
f[i][j] 的值是由 f[i - 1][j],f[i - 1][j - w[i]] 来推导出来的,也就是 f[i][j] 的取值只与前面的状态有关系。换句话说,所有的状态都是从以前的状态来推导出来的。再换句话说,所有的有效状态是从之前的有效状态和无效状态推导出来的!!!
接下来是重点!!!
接下来是重点!!!
接下来是重点!!!

如果无效状态无法推出有效状态,我们就不必关心无效状态(背包没装满)时候,背包内物体的总价值。
因为背包恰好装满时候我们只关心有效状态,而无效状态又无法推出有效状态。
也就可以把所有无效状态时背包的总价值都设置为负无穷。

我们来证明下无效状态无法推出有效状态。
对于下面的状态转移方程

f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);

(1)如果f[i - 1][j]和 f[i - 1][j - w[i]] 都是有效状态,那么f[i][j]是由有效状态推出的。

(2)如果f[i - 1][j]是无效状态, f[i - 1][j - w[i]] 是有效状态:
此时f[i][j]背包容量为j,f[i - 1][j - w[i]] 背包容量为j - w[i]且是恰好装满的。从f[i - 1][j - w[i]] 变换到f[i][j]相当于向一个已经装了总体积为j - w[i]物体的背包中放入一个体积为w[i]的物体,新的容量为j与当前背包容量j相同!!!
刚才我们说了无效状态可以看作负无穷,于是 f[i - 1][j - w[i]] + v[i]作为有效状态一定大于f[i - 1][j],所以可以得到新的有效状态f[i][j]。
所以此时f[i][j]是有效状态且是由有效状态f[i - 1][j - w[i]]推出的。

(3)如果f[i - 1][j]是有效状态, f[i - 1][j - w[i]] 是无效状态:
f[i - 1][j - w[i]] 是无效状态说明当前的背包内装的物体体积小于j - w[i],再放入一个体积为w[i]的物体后容量小于j,也就是说f[i - 1][j - w[i]] + v[i]也是无效状态。
此时因为f[i - 1][j]是有效状态,所以一定大于无效状态f[i - 1][j - w[i]] + v[i],则f[i][j]是一个由效状态f[i - 1][j]推导出的有效状态。
所以此时f[i][j]是有效状态且是由有效状态f[i - 1][j]推出的。

综上,只有有效状态可以推出有效状态。

2.3 无效状态的处理方法

由于c++中最小的负整型为16进制数 0x80000000,我们无法做到真正的负无穷。于是为了在计算无效状态时候依然采用同样的状态转移方程,我们需要用点小手段,即

#define INF 0x80000000
for (int j = V; j >= v[i]; j--)
{
	f[j] = max(f[j], f[j - v[i]] + w[i]);
	if (f[j] < 0)
		f[j] = INF;
}

如果算出来的是无效状态,强制赋值为0x80000000。
这样就相当于模拟了负无穷加上一个有限的正整数的计算过程,即负无穷加上任意有限正数还是负无穷。

3. 01背包恰好装满的代码实现

综上,我们发现,恰好装满与不是恰好装满只是在初始化过程上有差别而已。只需要在初始化时候把所有的无效状态初始化为负无穷就可以了。但是,f[0]需要初始化为0,因为背包容量为0时,装的物体体积为0是有效状态!

//01背包--恰好装满
#include <iostream>
using namespace std;
#define max(N1,N2) N1>N2?N1:N2
#define INF 0x80000000
int main()
{
	int V, N;
	while (cin >> V >> N)//输入背包容量和物体数
	{
		int v[1000], w[1000];
		int f[10000] ;
		//下面是初始化所有的无效状态
		for (int i = 0; i < 10000; i++)
		{
			f[i] = INF;
		}
		//f[0]是有效状态
		f[0] = 0;
		//输入每个物体的体积和价值
		for (int i = 1; i <= N; i++)
		{
			cin >> v[i] >> w[i];
		}
		//动态规划过程
		for (int i = 1; i <= N; i++)
		{
			for (int j = V; j >= v[i]; j--)
			{
				f[j] = max(f[j], f[j - v[i]] + w[i]);
				if (f[j] < 0)
					f[j] = INF;
			}
		}
		//判断能否恰好装满背包
		if (f[V] > 0)
		{
			cout << f[V] << endl;//背包恰好装满了,输出结果
		}
		else
		{
			cout << "error" << endl;//背包不能恰好装满
		}
	}
	return 0;
}
C++ 算法篇 动态规划----背包之一 01背包 一、01背包 题目:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 w[i],价值是 p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 例,有3个物品,背包容量为10,如下: 二维解法: #include<bits/stdc++.h> using namespace std; int f[50][220]={... 阅读详情

相关推荐

算法之01背包问题和完全背包问题

01背包问题,多重背包问题,完全背包问题的详细描述(适合有基础的人看): 初始化合法状态解释:背包问题的dp数组的初始化其实就是合法状态的情况,就是在没有任何物品可以放入背包是的合法状态。假如说题目要求背包恰好装满的话,那么此时只有容量为0时的背包可以在什么也不装的状态下“恰好装满”,此时背包价值为0,其他容量的背包都没有合法的解,属于未定义的状态,所以我们就要定义非法值,通常为负无穷大,正无穷大,或者足够大或小的值,从而保证当前的合法解,一定是从之前的合法状态推导而来的。 正逆序解释:对于二维数组背包来说

有梦想的小新人的博客 1697

01背包 完全背包是否恰好装满问题

恰好装满问题 是否恰好装满的解法不同只在于初始值的不同 恰好装满: 求最大值时,除了dp[0] 为0,其他都初始化为无穷小 -0x3f3f3f3f 求最小值时,除了dp[0] 为0,其他都初始化为无穷大   0x3f3f3f3f 不必恰好装满: 全初始化为0   初始化的细节问题(参考:链接) 我们看到的求解最优解的背包问题中,事实上有两种不太相同的问法。  1. 要求”背包恰好...

Simple的博客 8204

动态规划入门:从01背包问题理解状态转移与空间优化

动态规划是解决最优化问题的核心算法范式,其核心思想在于将复杂问题分解为重叠子问题,并通过存储子问题的解来避免重复计算,从而提升效率。理解状态定义、状态转移方程和最优子结构是其关键。在工程实践中,动态规划广泛应用于资源分配、路径规划和序列比对等场景。01背包问题作为动态规划的经典教学模型,完美诠释了这些概念:给定背包容量和一组物品(每个物品有体积和价值),求解在不超过背包容量的前提下如何选择物品使得总价值最大。通过从暴力递归到记忆化搜索,再到自底向上的递推(二维DP),最终通过一维滚动数组进行空间优化,这一完

weixin_42519781的博客 348

背包问题的恰好装满问题

在一些背包问题中要求会从不超过背包最大容量变为恰好装满背包,与前者的差别在于初始化的不同

weixin_44003265的博客 7153

GESP六级真题解析:从“道具商店”看01背包动态规划

背包问题是动态规划中最经典的组合优化模型,核心是在有限容量下从若干物品中做出选择,使总价值最大。其原理源于状态定义与状态转移方程:面对每件物品,只需比较“选”与“不选”的收益,并通过滚动数组优化空间复杂度。掌握这一技术价值不仅在于应对算法竞赛,还能迁移到资源分配、投资组合等真实工程场景。以GESP 2025年12月C++六级真题“道具商店”为例,题目将购买金币道具抽象为典型的01背包问题,许多考生因忽略“最多购买一件”“金币不需花完”等关键约束而失分。本文从建模思路到满分代码完整拆解,重点分析初始化差异、逆

weixin_42524276的博客 291

背包问题【01、完全(恰好or不超过)、多重】【尚未整理完】

变形问题:2N长度数组,分成俩N长度的,使其和差值最小。【程序员面试100题之十五:数组分割】 (写在前面)背包问题 总结的要点: (黄色字体标题的代码为三个问题最终用的模板) 1.一维(矩阵)的01背包为啥要逆序(保证更新f[j]时,f[ j - weight[i] ]是没有放入物品i时的数据即f[ i - 1 ][ j - weight[i] ],因为01背包每个物品至...

Dr_Unknown的博客 6823

【算法学习计划】动态规划 -- 背包问题(01背包和完全背包)

【算法学习计划】动态规划 -- 背包问题(01背包和完全背包) DP41 【模板】01背包 leetcode 416.分割等和子集 leetcode 494.目标和 leetcode 1049.最后一块石头的重量Ⅱ DP42 【模板】完全背包 leetcode 322.零钱兑换 leetcode 518.零钱兑换Ⅱ leetcode 279.完全平方数

2302_80023639的博客 1344

从零构建:Trinamic API在STM32上的深度移植与TMC-IDE通信实战

本文详细介绍了如何从零开始在STM32平台上深度移植Trinamic API,并实现与TMC-IDE工具的稳定通信。内容涵盖SPI底层驱动定制、寄存器配置、高级功能实现及调试技巧,重点解析了TMC5160芯片的集成实践,帮助开发者构建可靠的嵌入式运动控制系统。

w1x2y3的博客 254

蓝桥杯必考:背包问题全攻略,从01背包到多重背包

动态规划是算法竞赛中的核心思想,背包问题作为其经典分支,广泛存在于资源分配、任务调度等现实场景中。通过状态设计与转移方程,可以系统解决各类最优决策问题。文章深入剖析三类背包模型:01背包要求每件物品最多选一次,完全背包允许无限选取,多重背包则设定数量上限,并重点讲解滚动数组的遍历顺序、二进制拆分优化等实用工程技巧。结合蓝桥杯真题,展示如何快速识别背包模型、规避常见踩坑点,帮助读者从基础DP入门逐步进阶到竞赛实战,建立起一套完整的背包问题解题框架。

weixin_33806859的博客 175

别再死磕U-Net了!用PyTorch手把手教你搭建ResUnet,搞定医学图像分割(附完整代码)

本文详细介绍了如何利用PyTorch搭建ResUnet模型,通过引入残差连接解决医学图像分割中的梯度消失、特征退化和细节丢失问题。与传统U-Net相比,ResUnet在Dice系数和收敛速度上均有显著提升,特别适合处理脑部MRI等复杂医学图像。文章还提供了完整的代码实现和关键训练技巧,帮助开发者快速掌握这一先进技术。

weixin_42552637的博客 327

LangChain开发避坑指南:为什么Pylance总提示‘无法解析导入‘?实测7种修复方案

本文深入解析了在VS Code中使用LangChain开发时,Pylance频繁提示‘无法解析导入’的根本原因——Python解释器与语言服务器的环境路径不一致。文章提供了从基础排查到深度配置的7种实测修复方案,包括切换解释器、配置extraPaths、使用pyrightconfig.json等,旨在帮助开发者彻底解决此问题,提升AI应用开发效率。

weixin_28826521的博客 447

01背包详解:动态规划、滚动数组优化与C++实战

动态规划是算法面试与竞赛中的核心思想,而01背包正是理解DP本质的最佳入门模型。它通过状态定义和状态转移方程,将“选或不选”的决策分解为可复用的子问题,配合二维表格逐步推导出最优解。面对大规模数据,一维滚动数组优化能在保证正确性的同时大幅降低空间复杂度,但内层倒序遍历是防止物品被重复选用的关键。初始化细节、恰好装满与不超过容量的区别,以及完全背包、多重背包等变种,也都在此基础上自然延伸。背包模型不仅活跃在OJ题目中,更广泛用于资源分配、物流装箱、营销预算优化等真实场景。本文用C++从零拆解01背包的完整实现

weixin_33802505的博客 375

html一号店项目代码,项目一号店素材(html模板)

【实例简介】【实例截图】【核心代码】oneShop├── css│ ├── ShopShow.css│ ├── base.css│ ├── load.css│ ├── logo.css│ ├── register.css│ └── style.css├── detail.html├── images│ ├── Thumbs.db│ ├── a_close.png│...

weixin_35390150的博客 4989

动态规划入门:01背包问题详解与代码实现

动态规划(DP)是解决最优化问题的经典算法范式,通过将问题分解为子问题并存储中间结果来提高效率。01背包作为DP的入门案例,展现了状态转移方程的核心思想:定义dp[i][j]表示前i件物品在容量j时的最大价值,通过比较装与不装当前物品的收益进行状态更新。该算法在资源分配、投资组合等场景有广泛应用,空间优化后仅需O(V)复杂度。掌握背包问题对理解完全背包、多重背包等变种至关重要,也是AcWing等算法平台的热门训练题目。

weixin_32183107的博客 246

【MicroPython编程-ESP32篇:设备驱动】-SHT31温度湿度传感器驱动

SHT31 是 Sensirion 的下一代温湿度传感器。 它建立在一个新的 CMOSens® 传感器芯片之上,该芯片是 Sensirion 新湿度和温度平台的核心。

视觉与物联智能 229

FineReport FCP模拟题五

新建决策报表,设置 移动端 格式,采用绝对布局。数据来源:FRDemo中的订单表/雇员表。

Davina_yu的博客 725

01背包问题全解析:从暴力搜索到动态规划与空间优化

动态规划是算法面试中的核心思想,用于解决具有重叠子问题的最优化问题。01背包作为最经典的DP模型,要求每件物品只能选一次,在容量限制下最大化总价值。理解状态定义与状态转移方程,是掌握动态规划的关键。通过从暴力搜索到DP的演进,可以梳理出重叠子问题的优化思路,而滚动数组与倒序遍历等空间优化技巧,则展现了从二维到一维的工程落地方法。该模型广泛应用于资源分配、装箱调度等场景,也是完全背包、分组背包等变形问题的基础。本文以01背包为主线,结合代码实现与边界细节,帮助读者真正理解动态规划的本质,从容应对算法面试中的高

weixin_32667955的博客 294

在最新版eclipse中配置使用AI AIcoder _AIcoder配置GPUSTACK使用_配置OLLAMA本地AI模型利用_集合---AI场景应用工作笔记

本文介绍了三款实用的编程辅助工具:一款便捷的反编译插件,可直接在插件市场安装使用;GitHub Copilot提供每月2000次代码补全和50条聊天记录的免费额度,支持智能代码提示功能;aicoder支持配置本地模型运行。这些工具都能有效提升开发效率,其中GitHub Copilot的智能补全功能尤为突出。

添柴程序猿的专栏 1246

从采药到01背包:动态规划状态转移与空间优化全解析

动态规划是解决资源分配与最优化问题的核心算法思想,其本质是把大规模决策拆解为重叠子问题并记录中间结果。01背包作为最经典的动态规划模型,常通过状态转移方程描述“选”与“不选”的决策过程,配合滚动数组和逆序枚举实现空间优化。这一思想广泛应用于采药、排期、投资组合等真实场景。以P1048采药题为载体,从二维DP到空间优化逐层拆解01背包的建模与实现细节,帮助读者彻底理解状态定义、转移方程及常见变形。

weixin_34005090的博客 65

信号处理算法:离散余弦变换(DCT)_(1).离散余弦变换(DCT)概述

离散余弦变换的数学定义如下:对于一个长度为NNN的离散信号xnx(n)xn,其DCT变换后的系数XkX(k)XkXk∑n0N−1xncos⁡πNn12kk012N−1Xkn0∑N−1​xncosNπ​n21​kk012N−1其中,kkk是频率指数,nnn是时间或空间索引。离散余弦变换(DCT)是一种强大的信号处理技术,广泛应用于图像压缩、音频处理、数据压缩等领域。

2401_87715305的博客 1554

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

C++动态规划入门:01背包问题从暴力递归到一维优化的完整套路

动态规划是算法竞赛与工程面试中的核心思想,其本质是用空间换时间,通过记录子问题结果避免重复计算。理解子问题重叠与无后效性,是掌握状态转移方程的前提。01背包问题正是学习这类思想的经典入门题:每件物品选或不选,在容量限制下求最大价值。暴力枚举复杂度高达O(2^n),而基于状态转移的二维DP可将复杂度降为O(n*C),再借助滚动数组进一步优化空间至O(C),这也是资源分配、物流调度等场景的通用模型。从暴力递归到记忆化搜索,再到一维倒序遍历,每一步都蕴含着DP的底层设计思路,尤其倒序遍历防止重复选取的关键细节,能

weixin_27442001的博客 209

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

动态规划——01背包问题

一、最基础的动态规划之一 01背包问题是动态规划中最基础的问题之一,它的解法完美地体现了动态规划的思想和性质。 01背包问题最常见的问题形式是:给定n件物品的体积和价值,将他们尽可能地放入一个体积固定的背包,最大的价值可以是多少。我们可以用费用c和价值v来描述一件物品,再设允许的最大花费为w。只要n稍大,我们就不可能通过搜索来遍查所有组合的可能。运用动态规划的思想,我们把原来的问题拆分为子问题...

weixin_34023863的博客 207

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

LeetCode 494. Target Sum【0-1背包变形+恰好装满+可行解】⭐⭐⭐⭐⭐

文章目录题目描述知识点结果实现码前思考代码实现码后反思 题目描述 知识点 动态规划 结果 实现 码前思考 我这道题目没有想出来,直接看的题解,实在是想不出。 原来这道题目就是0-1背包,通过 dp数组保存前n个物品的所有可能和的方案数 ,从而避免了重复计算。避免了暴力的O(2n)O(2^n)O(2n); (我有想到暴力O(2n)O(2^n)O(2n),但是不知道怎么优化它,现在想来,当初0-1背包问题的提出也是为了优化O(2n)O(2^n)O(2n)啊!!!) 代码实现 //这居然是一个0-1背.

yc_cy1999的博客 428

01背包问题、优化及变形

01背包问题的动态规划实现以及优化;考虑是否需要恰好装满背包时的情况。

qq_34826261的博客 569

盖子的c++小课堂——第二十七讲:背包变形题

记得点赞关注加收藏~~~ღ( ´・ᴗ・` )比心

盖子小课堂 1157

背包问题——“01背包”及“完全背包”装满背包的方案总数分析及实现

-----Edit by ZhuSenlin HDU         本人博文《背包问题——“01背包”最优方案总数分析及实现》及《背包问题——“完全背包”最优方案总数分析及实现》中分别谈过“01背包”和“完全背包”实现最大价值的方案总数,这里我们再讨论一下这两种背包被物品刚好装满的方案总数。         网上各大公司经常出题目:假设现在有1元、2元、5元的纸币很多张,现在需要20块钱,你

wumuzi520的专栏 2万+
上一篇: 深入理解堆与堆排序
下一篇: Logistic回归详解
Iseno_V
博客等级 码龄9年 60粉丝 12原创
评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值