背包问题
词义:给定一组物品,每种物品都有自己的重量和价格。在限定的总重量内,如何选择,才能使得物品的总价格最高。
分类:
先来讲01背包(基础 -> 优化)
截我回目录
例题:分蛋糕
基础思路:
乍一看,没啥思路。。。
提示:
为了尽量的是他们两人手中的蛋糕重量总和的差值最小,我们就可以让其中某一个人的蛋糕重量正好达到 (总重量的一半)。
然后这就变成了一个01背包问题:放?还是不放???
转化后的思路:
先获取 的状态: 表示第i个数能不能正好放进容量为j的背包中
确定转移方程: ->
最后一行从后往前看:如果能装,就用sum减容量(j),再减j,就得出了结果。
优化
既然是01背包,那么就一定可以做空间优化
我们可以用滚动数组来做……
(不解释,直接上代码)
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, a[105], dp[2][5000005], sum = 0, ans = 0;
int main() {
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i]; //算sum的值
}
ll V = sum / 2;
dp[0][0] = 1; //赋初值(至少也得有一种吧)
for (ll i = 1; i <= n; i++) {
for (ll j = 0; j <= V; j++) {
if(j < a[i]) dp[i % 2][j] = dp[(i - 1) % 2][j]; //判断空间是否足够,若不够,不放
else if(dp[(i - 1) % 2][j - a[i]] || dp[(i - 1) % 2][j]) dp[i % 2][j] = 1; //放
else dp[i % 2][j] = 0; //不放
}
}
for (ll j = V; j >= 0; j--) { //搜寻答案
if(dp[n % 2][j]) {
ans = j;
cout << (sum - ans) - ans << endl; // 得到答案
return 0;
}
}
return 0;
}
(重点来了)坑点讲解
这道题坑点多多,是一道名副其实的坑题
空间优化前的代码放上~~
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, a[105], dp[102][5000002], sum = 0, ans = 0;
int main() {
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i]; //算sum的值
}
int V = sum / 2;
dp[0][0] = 1; //赋初值(至少也得有一种吧)
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= V; j++) {
if(j < a[i]) dp[i][j] = dp[i - 1][j]; //判断空间是否足够,若不够,不放
else if(dp[i - 1][j - a[i]] || dp[i - 1][j]) dp[i][j] = 1; //放
}
}
for (int j = V; j >= 0; j--) {
if(dp[n][j]) {
ans = j;
cout << (sum - ans) - ans << endl;
return 0;
}
}
return 0;
}
因为此题要求的数最大可以是 ,可以有 个数,经作者计算后,发现一半就有 ,结果数组开不下了。。。
若dp[0][0]没有赋初值为1,那么程序跑出来就全都是0。
记住:j < a[i] 这个判断一定要加!不然的话。。。 (看下图)
else if 后面的条件是两个,一个是取的结果,一个是不取的结果。
最后求答案一定是反着来,因为你要的值越大越好。
作者最后发现这道题可以卡过去,因为数组可以“卡入膏霜”。。。 (看下图 + 代码)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, a[105], dp[102][2624004], sum = 0, ans = 0; // 注意dp数组,他可是重点(卡出来的)
int main() {
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
}
int V = sum / 2;
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= V; j++) {
if(j < a[i]) dp[i][j] = dp[i - 1][j];
else if(dp[i - 1][j - a[i]] || dp[i - 1][j]) dp[i][j] = 1;
}
}
for (int j = V; j >= 0; j--) {
if(dp[n][j]) {
ans = j;
cout << (sum - ans) - ans << endl;
return 0;
}
}
return 0;
}
虽然可以卡过去,但是建议大家最好还是做一下优化,以防空间不足,数组越界的问题。
编译错误显示:
/tmp/ccI3ZDbs.o: In function `main':
a.cpp:(.text.startup+0x42): relocation truncated to fit: R_X86_64_PC32 against symbol `n' defined in .bss section in /tmp/ccI3ZDbs.o
a.cpp:(.text.startup+0xbb): relocation truncated to fit: R_X86_64_32S against symbol `a' defined in .bss section in /tmp/ccI3ZDbs.o
collect2: error: ld returned 1 exit status
所以,做空间优化还是非常非常重要的。。。
最后,思路说清楚了,大家也一定非常明白了吧。
(进入下一节)
多重背包(基础 -> 优化)
截我回目录
未完待续
完全背包(基础 -> 优化)
截我回目录
未完待续