三种背包问题(部分背包、0-1背包、0-k背包(完全背包))-贪心算法、动态规划。

目录

 

贪心算法:

部分背包:

动态规划:

0-1背包:

二维数组:

滚动数组:

完全背包:

滚动数组:

01背包、完全背包区别:

       二维数组:

       滚动数组:


贪心算法:

部分背包:

给定一个最大载重量为M的卡车和N种食品,有食盐、白糖、大米等。已知第i种食品最多 拥有Wi千克,其商品价值为Vi元/千克,编程确定一种装货方案,使得装入背包中的所有物 品总价值最大。

特点:这些物品都是可以任意装下的。

例一

思路:显然单价越高的越先填,到最后只剩下一部分空间再塞满剩余中最贵的物品即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
struct bag{
	int m;
	int v;
	double w;
}b[150];//封装每个物品的价格 重量以及单价
bool cmp(bag x,bag y){
	return x.w > y.w;
}
int main(){
	int n,t;
	cin >> n >> t;
	for(int i = 0;i < n;i++){
		cin >> b[i].m >> b[i].v;
		b[i].w = 1.0*b[i].v/b[i].m; 
	}
	sort(b,b+n,cmp);
	double val = 0;
	int i;
	for(i = 0;i < n;i++){
		if(b[i].m > t) break;
		t-=b[i].m;val += b[i].v;
	}
	val +=(b[i].w*t);	//需要把最后剩余的空间填满
	printf("%.2lf",val);
	return 0;
}

 

动态规划:

0-1背包:

例二

注意事项:

                1:需要在j<sp[i]复制上一层的i,为了dp[i+1]位置用到小于sp[i]的时候

                 2.递推式:dp[i][j]表示前i的物品种花费j时间所能得到的最大价值。dp[i][j]=max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]) 不选的情况 dp[i-1][j] ,选的情况就是dp[i-1][j-sp[i]]+w[i]也就是前上一个物品腾出来采这种药材的时间,再加上新物品的价格。

二维数组:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int w[150];
int sp[150];
int dp[150][1050];
int main(){
	int t,m;
	cin >> t >> m;
	for(int i = 1;i <= m;i++){
		cin >> sp[i] >> w[i];
	}
	for(int i = 1;i <= m;i++){
		for(int j = 0;j <= t;j++){
			if(j < sp[i]) dp[i][j] = dp[i-1][j];
		 	else dp[i][j] = max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]);
		} 
	}
	for(int i = 0;i <= m;i++){
		for(int j = 0;j <= t;j++)
			cout << dp[i][j];
		cout << endl;
	}
	cout << dp[m][t] << endl;
	return 0;
}

滚动数组:

顾名思义,滚动i进行更新j。dp[j]表示花费j时间所能达到的最大价值

递推式:dp[j]表示花费j时间可以获得的最大价值。dp[[j]=max(dp[j],dp[j-sp[i]]+w[i]);for从后往前遍历j。不能从前往后,当j<sp[i]是不需要变化dp[j],所以for范围从t到sp[i]即可。不选i的情况直接照搬选择i-1即可,也就是dp[j],选i的情况那么就是dp[j-sp[i]]+w[i]腾出来sp[i]时间给w[i]价值的i药品。从后往前能够利用好之前的数据,如果从前往后的话,第一个数据被更新掉,后面如果用到了更新掉的数据就不是只选择一次了,就是选择两次了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int w[150];
int sp[150];
int dp[1050];
int main(){
	int t,m;
	cin >> t >> m;
	for(int i = 1;i <= m;i++){
		cin >> sp[i] >> w[i];
	}
	for(int i = 1;i <= m;i++){
		for(int j = t;j >= sp[i];j--)
			dp[j] = max(dp[j],dp[j-sp[i]]+w[i]);
	} 
	cout << dp[t] << endl;
	return 0;
}

完全背包:

例三

滚动数组:

          更改一个地方代码即可,但是数据范围有变 所以数组要开大 并且用long long。

          递推式:dp[j]表示花费j时间可以获得的最大价值。dp[j]=max(dp[j],dp[j-sp[i]]+w[i]) 从前往后,这样的话可以更新前面的新数据,从后往前,后面使用前面的,但前面的是i-1物品最优的,未必是前i最优的。       

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e7+50;
const int maxm = 1e4+50;
ll w[maxm];
ll sp[maxm];
ll dp[maxn];
int main(){
	ll t,m;
	cin >> t >> m;
	for(int i = 1;i <= m;i++){
		cin >> sp[i] >> w[i];
	}
	for(int i = 1;i <= m;i++){
		for(int j = sp[i];j <= t;j++)
//dp[j] = max(dp[j],dp[j-sp[i]]+w[i]);
			dp[j] = max(dp[j],dp[j-sp[i]]+w[i]);
	} 
	cout << dp[t] << endl;
	return 0;
}

二维数组数据太大 不变操作。否则也只更改一个地方即可:

       for(int i = 1;i <= m;i++){

              for(int j = 0;j <= t;j++){

                     if(j < sp[i]) dp[i][j] = dp[i-1][j];

                    //else dp[i][j] = max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]);

                            else dp[i][j]=max(dp[i-1][j],dp[i][j-sp[i]]+w[i]);

              }

       }

01背包、完全背包区别:

       二维数组:

                           01背包递推式:dp[i][j] = max(dp[i-1][j],dp[i-1][j-sp[i]]+w[i]);

                           完全背包递推式:dp[i][j]=max(dp[i-1][j],dp[i][j-sp[i]]+w[i]);

发现,第一个不选i的是一样的 都是dp[i-1][j]表示不选也就是前i-1药品花费j时间。不同的是第二个。01背包中为dp[i-1][j-sp[i]]+w[i]

表示选的话就等于前i-1个空出来sp[i]也就是花费j-sp[i]可以获得最大价值加上w[i]。i-1是因为一个物品只能被选择一次,所以不可以是i,而完全背包是多次选择,所以完全背包应该等于前i个空出来sp[i]也就是j-sp[i]可以获得的最大价值加上w[i]。

       滚动数组:

dp[j]=max(dp[j],dp[j-sp[i]]+w[i]),01背包和完全背包的递推式一样,唯一的区别在于for循环的前后顺序。i被省略,也就是i滚动更新dp[j]。因为如果选择i的话应该是dp[j-sp[i]]+w[i]。如果从前往后,每一次更新都是用的最新的数据,也就是前i的物品的最优解,所以是完全背包,如果01背包也这样的话,会出现一个物品多次选择,比如第一个更新后的数据,第二个更新的时候用到了第一个更新后的,那么第二个的最优解就选择了两次i物品。从后往前,每一次用的都是前i-1药品的数据,所以只被选了1次。

 

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值