当前位置: 首页 > article >正文

动态规划之背包问题

动态规划是一个重要的算法范式,它将一个问题分解为一系列更小的子问题,并通过存储子问题的解来避免重复计算,从而大幅提升时间效率。

目录

01背包问题

完全背包问题

多重背包问题

二维费用背包问题


(1)01背包问题

给定n个物体,和一个容量为c的背包,物品i的重量为wi,其价值为应该如何选择装入背包的物品使其获得的总价值最大。

可以用贪心算法,但是不一定能达到最优解,所以用动态规划解决

创建一个数组 dp[i][j] i为物品,j为容量,代表的值为价值

接下来要做的就是求状态转移方程

两种情况选择拿和不拿,假设w[i]为第i个的重量,c[i]为第i个的价值

推导过程如下

 所以最后的状态转移方程为

dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])

 所以该题的解为

#include<iostream>
#include<algorithm>
using namespace std;
int dp[35][35];
int w[35], c[35];
int main() {
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i] >> c[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (j < w[i])
				dp[i][j] = dp[i - 1][j];
			else
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]]+ c[i]);
		}
	}
	cout << dp[n][m];
	return 0;

}

 也可以进行优化,因为dp的值只和上一个数据相关,所以可以循环不断更新新数据

dp[j]=max(dp[j],dp[j-w[i]]+c[i])

所以优化后完全状态的01背包代码就为(j要从后往前推,不然上一次数据可能被吞掉)

#include<iostream>
#include<algorithm>
using namespace std;
int dp[205];
int w[35], c[35];
int main() {
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i] >> c[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = m; j>=1; j--) {
			if (j > w[i])
				dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
		}
	}
	cout << dp[m];
	return 0;

}

(2)完全背包问题

直接上题

假设有种物品,每种物品有一个重量及一个价值,但每种物品的数量是无限的,同时有一个背包,最大载重量为M,从n种物品中选取若干件(每种物品可以多次放入)使其重量小于等于M,而获得的价值最大。

完全背包与01背包的区别就是完全背包的物品数量是无限的

先来个简单的方法:不断往背包中装入物品,直到背包装满

当前背包的容量j/w[i]

在01背包中加一个k循环,决定拿k个i物品,然后求价值的最大值

即for(k=0;k<i/w[i];k++) dp[j]=max(dp[j],dp[j-k*w[i]]+k*c[i])

#include<iostream>
#include<algorithm>
using namespace std;
int dp[205];
int w[35], c[35];
int main() {
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i] >> c[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = m; j>=1; j--) {
			for (int k = 0; k < i / w[i]; k++)
				dp[j] = max(dp[j], dp[j - k * w[i]] + k * c[i]);
		}
	}
	cout << dp[m];
	return 0;

}

(但是这样做时间复杂度太高了,所以我们需要再优化一下)

优化后的状态转移方程为

dp[i][j]=max(dp[i][j],dp[i][j-w[i]]+c[i])

与01背包的区别就是从i开始而不是从i-1不需考虑上一层的状态

再将二维数组压缩成一维数组

dp[j]=max(dp[j],dp[j-w[i]]+c[i])

方程与01背包一样,但是应该从顺向推导,用到的是新数据

所以完全背包的终极写法为

#include<iostream>
#include<algorithm>
using namespace std;
int dp[205];
int w[35], c[35];
int main() {
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i] >> c[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j<=m; j++) {
			if(j>=w[i])
				dp[j] = max(dp[j], dp[j -  w[i]] +  c[i]);
		}
	}
	cout << dp[m];
	return 0;

}

(3)多重背包问题

再来个小题

有n种物品和一个容量为v的背包,第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

 与前两种背包的区别是每种有限件

用完全背包的思维,不断拿第i个物品个数小于等于n[i],但是总重量小于背包容量

#include<iostream>
#include<algorithm>
using namespace std;
int dp[6100];
int w[510], v[510],s[510];
int main() {
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= n; i++) {
		cin >> v[i] >> w[i]>>s[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = m; j>=1; j--) {
			for (int k = 0; k <= s[i] && j >= k * v[i]; ++k) {
				dp[j] = max(dp[j], dp[j - k * v[i]] + k * w[i]);
			}
		}
	}
	cout << dp[m];
	return 0;

}

依旧是熟悉的配方,我们来个优化(时间复杂度太高,当s很大时根本过不了)

多重背包的二进制优化(嘿嘿,不会)

(4)二维费用背包

有N件物品和一个容量为V的背包,背包能承受的最大重量是M,每件物品只能用一次,体积是ai,重量是bi,价值是wi。求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大

 01背包的变种,对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值

我们设dp[i][j][k],意义为:拿到第i个物品的情况下,双重循环遍历每一个代价j和k时的最大值

所以状态转移方程式为

dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-a[i][k-b[i]]+w[i])

同样采用01背包压缩数组状态转移方程式,优化时要逆向递推

dp[j][k]=max(dp[j][k],dp[j-a[i]][k-b[i]]+w[i]);

(主包觉得这样的代码还是太吃操作了,主包决定下次再完成这个代码,觉得主包写的好的可以点点赞,觉得不好也没关系,主包还是觉得很好的,下播)


http://www.kler.cn/a/553678.html

相关文章:

  • 力扣-二叉树-235 二叉搜索树的最近公共祖先
  • 位运算,双指针,二分,排序算法
  • 一台服务器将docker image打包去另一天服务器安装这个镜像
  • 2025年02月18日Github流行趋势
  • 【基础架构篇九】《DeepSeek模型版本管理:Git+MLflow集成实践》
  • MySQL面试考点汇总
  • 基于SpringBoot+Vue的老年人体检管理系统的设计与实现(源码+SQL脚本+LW+部署讲解等)
  • 变相提高大模型上下文长度-RAG文档压缩-3.优化map-reduce(reranker过滤+社区聚类)
  • 零基础学QT、C++(三)魔改QT组件库(付源码)
  • 闲鱼IP属地为何频繁变化:深入解析与应对策略
  • Redis为什么速度快、性能高?
  • 基于YOLO11深度学习的果园苹果检测与计数系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
  • Transformer多头注意力并行计算原理与工业级实现:从数学推导到PyTorch工程优化
  • WebAssembly:现代Web开发的革命性技术
  • vue3和vue2的组件开发有什么区别
  • MySQL标识列
  • 内核数据结构用法(5)hlist
  • 结构风荷载理论与Matlab计算
  • 什么是tomcat
  • Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符