神奇的背包问题

首先从两个背包说起......

虚假的背包:

 负重有限

物品占空间大

无法快速确定物品价值

大量占用人类生存空间

真正的背包:

0/1背包

完全背包

多重背包

...........................................................................................................................................................

负重无限

物品占用空间很小

物品价值确定快

不占用人类生存空间

这才是真正的背包!

好了,不多说,直接进入正轨——

————————————————————————————————————

背包问题看似🐮,其实万变不离其宗:

背包问题最基本的三种类型

01背包

其实是0/1背包,表示一个物品放进去了还是没放,但本身还是动态规划,只要不输出位置,bool和它没有关系

不分物品种类,数量有限制

完全背包物品种类有限制,数量无限制
多重背包

物品种类有限制,数量有限制

也就是集01加上完全

ps:完全、多重背包除不同点外,均与01背包相同!(请铭记这句话)

以上表格仅供参考,一切以实物为主......

01背包

解决背包题,首先你要记住:

背 包 基 本 都 是 动 态 规 划(类似于递推)

所以不用想别的啦!专心打表吧!

原理:

先假设four个物品,int w[?]={2,3,6,5}

当然还有价值:int v[?]={6,3,5,9}

设背包容量为9

则可得出以下表格(ps:行对应物品数量,列对应背包现存价值):

这就是dp[4][9](数组大小看题目):

0123456789
00000000000
10000000000
20000000000
30000000000
40000000000

当然一开始都是0

一行一行考虑:

如果只放前1个(第一个),那么其价值是物品1的价值,容量只有2,再多也放不进去,

所以dp[1][2]=2的价值6,所以dp[1][2]=6,整行除0外都是6(此情况就此一种方案)

0123456789
00000000000
10066666666

再考虑放前两个(第一、二个):

第二个,重量是3,可以放在dp[2][3]

0123456789
00000000000
10066666666
20060+3

但要放第二个,第一个又受不了,

太挤了,3个空放不下!

怎么办?

此时我们有两种方案:

方案1:舍弃第一个,放入第二个;

方案2:不放第二个,第一个仍在。

如何判断是采取方案1还是方案2?

看价值。(说得跟个黑社会似的)

dp数组用于存价值,第一个和第二个数价值相比较,就能够判定。

那么这么看来,w[1]明显高于w[2];(6>3)

那么启用方案2.

0123456789
00000000000
10066666666
20066666666

但如果背包容量是5呢?

显然,我们要求的最优条件失效了:w[1]+w[2]=9,比6和3都大!

那么我们启用——

方案3:当容量足够,均可加入总价值。

那么考虑前i个数......

最终,得到的儿dp表格如下:

0123456789
00000000000
10066666666
20066699999
3006669991111
40066699101111

所以最后答案是:dp[4][9]=11,输出,AC

代码如下:(仅供参考,切勿抄袭!!!)

#include <bits/stdc++.h>
using namespace std;

int w[201], v[31], dp[201][201];

int main() {
    memset(dp, 0, sizeof(dp));
    int m, n;
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &w[i], &v[i]);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (w[i] > j)//当前容量小于该物品重量,装不下
                dp[i][j] = dp[i - 1][j];//沿袭上一情况
            else//当前容量大于该物品重量,装得下
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
                //装或不装,max判断巧取方案
        }
    }
    printf("%d\n", dp[n][m]);
    return 0;
}

真的那么简单吗?

不可能滴!

还有更简单滴!

空间优化——滚动数组

数据很大,空间要优化!

别急,先观察dp表——

从表格中可以发现,每一行都是先前一行推出来的

那么用新的一行覆盖原来的(一维数组进行滚动

就可以实现优化,实现AC

错误代码如下:

#include<bits/stdc++.h>
#include<algorithm>
using namespace std;

int w[10001],v[1001],dp[10001][1001];

int main(){
	memset(dp,0,sizeof(dp));
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=n;i++)
		for(int j=m;j>=w[i];j--)
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//省去if判断,直接max
	cout<<dp[m]<<endl;
	return 0;
}
//当然,它运行不了,有作者精心设计的“编译错误”,聪明的你能找出来吗?

朕乏了,完全和多重明天再弄吧......

路过的银呐,用行动告诉我你曾来过这里!🙂

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值