HBU训练营【动态规划DP】——0-1背包 (20分)(附与分治算法的异同)

给定n(n<=100)种物品和一个背包。物品i的重量是wi,价值为vi,背包的容量为C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。

输入格式:
共有n+1行输入: 第一行为n值和c值,表示n件物品和背包容量c; 接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。

输出格式:
输出装入背包中物品的最大总价值。

输入样例:
在这里给出一组输入。例如:

5 10
2 6
2 3
6 5
5 4
4 6

输出样例:
在这里给出相应的输出。例如:

15

动态规划算法的核心思想:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法。
动态规划与分治算法类似,其基本思想也是将带求解的问题分解成若干子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的,即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解。而分治法不是,比如汉诺塔问题,一个盘子就是一个盘子,两个就是两个,三个就是三个,互不相关,只是规律相同,动态规划可以通过填表的方式来逐步推进,得到最优解。分治可以递归求解或者迭代循环,毕竟规律相同。
背包问题:每次遍历得到的第i个物品(就像平时放东西一样,一样一样的放,探测,看能不能放下)根据w[i]和v[i]来确定是否将该物品放入到背包中,即对于给定的n个物品,设v[i]、w[i]分别为第i个物品的价值和重量,c为背包的总容量。再令dp[i][j]表示前i个物品(因为前0个物品都是0,这样i就正好是第i个物品对应的dp值)中能够装入容量为j时的背包中的最大价值,本来容量为定值c,但现在它是一个变化的,因为需要一直探测当背包容量为1、为2……为c的时候都能放下些什么。
还是一样,从1开始遍历,dp第一行和第一列全部为0(全局变量的设定已经保证这一点),第一行全为0,表示当商品数为0的时候,不管容量有多大,背包的总价值都为0;第一列全为0,表示当背包的容量为0的时候,不管有多少件物品,重量有多小,背包的总价值也都为0,因为放不进去呀。。

每次遍历都要看当前背包容量(注意是个变量,从1到c)与要放置的物品的重量之间的关系

如果当前商品的重量大于背包的容量,就把它上一个格子的放置情况复制下来,
因为上一个的情况已经是当时的优化结果,即满足最大价值的结果
当w[i]>j时,dp[i][j] = dp[i-1][j];

当当前商品的容量小于当前背包的容量时,说明可以尝试去装一下
(为什么是尝试呢???
	因为装该物品后的总价值可能比上一轮的总价值大,也可能小)
为什么商品容量都小于当前背包的容量了,还去尝试呢,那不肯定装不下吗???
	当然是目前背包容量减去待尝试放置物品的重量啊,只有这样才是在上一次最优解的基础上放啊
当j>=w[i]时,dp[i][j] = max{dp[i-1][j],dp[i-1][j-w[i]] + v[i]}
#include <iostream> 
using namespace std;
int w[101],v[101];
int dp[1001][1001];

int main(){	
	int n,c;
	cin >> n >> c; 
	for(int i = 1;i<=n;i++){
		cin >> w[i] >> v[i];
	}
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=c;j++){
			if(j>=w[i])
				dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			else{
				dp[i][j]=dp[i-1][j];
			}
		}
	}
	cout << dp[n][c];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45845039/article/details/107954195