背包问题_(DP经典),一,(01背包,填满背包)

背包系列已更新

一,(01背包,填满背包)

二,(多重背包)

三,(完全背包)

目录

一,01背包(对于一个物品,你要么全拿,要么不拿)

核心dp方程,dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+b[i])  //i为物品数量,j为空间,a[i]为物品占的空间,b[i]为物品价

  1, 二维 ac代码

2,优化压缩一维

二,填满背包(只有这个拿了且能把背包空间装满才拿,不然不拿)

1,二维基础

AC代码

2,一维优化(还是一样的道理,简洁又高效)


 

 

 

一,01背包(对于一个物品,你要么全拿,要么不拿)

核心dp方程,dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+b[i])  //i为物品数量,j为空间,a[i]为物品占的空间,b[i]为物品价

for (int i = 1; i <= n; ++i)for (int j = 0; j <= m; ++j)
		{
			if (j - a[i] >= 0)
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + b[i]);
			else dp[i][j] = dp[i - 1][j];  //当然,写上述方程需要你先装得下物品i再说,不行只能继承前面
		}

dp思想是我拿i个,那我只需要假设前面i-1个已经拿了最优,则拿第i个时,我判断,如果拿第i个,那么前面剩余空间为[j-a[i]],那么dp[i-1][j-a[i]]+b[i]就是前i-1个,[j-a[i]]的空间加上第i个的价值比上我不拿第i个的价值,取大值

明确后我们就可以从头写到尾

ca6daa063f5a4a3798147bcdb350caad.png

  1, 二维 ac代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
#define ll long long

const int N = 1100;
   //数组放外面默认初始为0
int dp[N][N];
int a[N];  //消耗猫粮
int b[N];//获得豆子


int main() {
	int m, n;  //m为猫粮(即我的空间,n为房间,即物品数量
	while (cin >> m >> n&&(n!=-1||m!=-1)) {
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= n; ++i)cin >> b[i] >> a[i];
		for (int i = 1; i <= n; ++i)for (int j = 0; j <= m; ++j)//表示选择第i个物品时,此时有j空间
		{     //j初始化为0,因为可能0空间背包也能装大小(占0空间大小的物品(滑稽))
			if (j - a[i] >= 0)
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + b[i]);
			else dp[i][j] = dp[i - 1][j];  //当然,写上述方程需要你先装得下物品i再说,不行只能继承前面
		}
		cout << dp[n][m] << endl;
	}


	return 0;
}

2,优化压缩一维

显然,当我N取1e6或者更大时,二维数组就爆了,而且,我们发现dp转移方程,都是i-1是不是有点多余,于是我们可以选择去除这个i,只保留j就好

 

核心

	for (int i = 1; i <= n; ++i)for (int j = m; j>=a[i]; --j) {//这里i放前面,j放后面,且j从大循环到小,保证一个i只拿一次,j从大到小,就保证本次j空间存入i,后面大空间不会再存一次(因为我从大空间到小空间),导致重复使用
			dp[j] = max(dp[j], dp[j - a[i]] + b[i]);           //j取到a[i] ,一是防止前面什么0空间还能拿的情况出现,二是你j小于a[i]了,也还是dp[j],不会更新,不用去浪费时间循环        //这样我dp[j]更新的都是上一组拿i-1个的,不会重复

 

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
#define ll long long

const int N = 1100;
   //数组放外面默认初始为0
int dp[N];
int a[N];  //消耗猫粮
int b[N];//获得豆子


int main() {
	int m, n;  //m为猫粮(即我的空间,n为房间,即物品数量
	while (cin >> m >> n&&(n!=-1||m!=-1)) {
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= n; ++i)cin >> b[i] >> a[i];
		for (int i = 1; i <= n; ++i)for (int j = m; j>=a[i]; --j) {//这里i放前面,j放后面,且j从大循环到小,保证一个i只拿一次,j从大到小,就保证本次j空间存入i,后面大空间不会再存一次(因为我从大空间到小空间),导致重复使用
			dp[j] = max(dp[j], dp[j - a[i]] + b[i]);           //j取到a[i] ,一是防止前面什么0空间还能拿的情况出现,二是你j小于a[i]了,也还是dp[j],不会更新,不用去浪费时间循环        //这样我dp[j]更新的都是上一组拿i-1个的,不会重复
			  
		}
		cout << dp[m] << endl;
	}


	return 0;
}

二,填满背包(只有这个拿了且能把背包空间装满才拿,不然不拿)

 

 

其实跟01背包没有很大区别,我们在dp转移加一个判断就好了,只要前面的dp[i-1][j]小于0,我就不装物品,因为他不是填满的。只有满的我才装

1,二维基础

AC代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
#define ll long long

const int N = 1100;
   //数组放外面默认初始为0
int dp[N][N];
int a[N];  //消耗猫粮
int b[N];//获得豆子


int main() {
	int m, n;  //m为猫粮(即我的空间,n为房间,即物品数量
	while (cin >> m >> n&&(n!=-1||m!=-1)) {
		memset(dp, -1, sizeof(dp));   //初始值赋值-1,设定他们没有填满
		dp[0][0] = 0;//当然,0空间的背包肯定是满的,这样就作为起点
		for (int i = 1; i <= n; ++i)cin >> b[i] >> a[i];

		for (int i = 1; i <= n; ++i)for (int j = 0; j <= m; ++j) {
			if (j - a[i] >= 0) {//首先空间要装的下,这是前提,否则你越负数空间访问得到的就不是正确答案了
				if (dp[i - 1][j - a[i]] >= 0)dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + b[i]);//只有前面dp为0或者大于0,我才赋值
				else dp[i][j] = dp[i - 1][j];//这里补一句,是因为,可能进了第一个if,但是第二个if又进不去,然后进来第一个if后面的else就不会去了,导致没有继承前面的dp[i][j] = dp[i - 1][j];
			}                          //所以写二维真的浪费空间,浪费时间,浪费脑力,好好学一维吧
			else dp[i][j] = dp[i - 1][j];
		}
		cout << dp[n][m] << endl;//如果没有被赋值到,那么他初始化就是-1,有就是答案
	}


	return 0;
}

2,一维优化(还是一样的道理,简洁又高效)

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
#define ll long long

const int N = 1100;
   //数组放外面默认初始为0
int dp[N];
int a[N];  //消耗猫粮
int b[N];//获得豆子


int main() {
	int m, n;  //m为猫粮(即我的空间,n为房间,即物品数量
	while (cin >> m >> n&&(n!=-1||m!=-1)) {
		memset(dp, -1, sizeof(dp));   //初始值赋值-1,设定他们没有填满
		dp[0] = 0;//当然,0空间的背包肯定是满的,这样就作为起点
		for (int i = 1; i <= n; ++i)cin >> b[i] >> a[i];

		for (int i = 1; i <= n; ++i)for (int j = m; j >= a[i]; --j) {   //j的范围大于等于a[i]就好,因为你小了肯定不用更新(一维又不需要继承前面i-1的数据)
			if (dp[j - a[i]] >= 0)dp[j] = max(dp[j], dp[j - a[i]] + b[i]);//可以就更新,不行不用管
		}
		cout << dp[m] << endl;
	}


	return 0;
}

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值