背包问题(未完待续。。。)

背包问题


词义:给定一组物品,每种物品都有自己的重量和价格。在限定的总重量内,如何选择,才能使得物品的总价格最高。

分类:


先来讲01背包(基础 -> 优化)

截我回目录

例题:分蛋糕

基础思路:

乍一看,没啥思路。。。

提示:

为了尽量的是他们两人手中的蛋糕重量总和的差值最小,我们就可以让其中某一个人的蛋糕重量正好达到 s u m / 2 sum / 2 (总重量的一半)。


然后这就变成了一个01背包问题:放?还是不放???



转化后的思路:

先获取 d p [ i ] [ j ] dp[i][j] 的状态: 表示第i个数能不能正好放进容量为j的背包中

确定转移方程: i f ( d p [ i 1 ] [ j v [ i ] ] ( ) d p [ i 1 ] [ j ] ( ) ) if(dp[i - 1][j - v[i]] (取) || dp[i - 1][j] (不取)) -> d p [ i ] [ j ] = 1 dp[i][j] = 1

最后一行从后往前看:如果能装,就用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;
}

因为此题要求的数最大可以是 1 0 5 10^5 ,可以有 100 100 个数,经作者计算后,发现一半就有 5 × 1 0 6 5 \times 10^6 ,结果数组开不下了。。。

若dp[0][0]没有赋初值为1,那么程序跑出来就全都是0。

记住:j < a[i] 这个判断一定要加!不然的话。。。 \downarrow (看下图)
背包问题错误示范

else if 后面的条件是两个,一个是的结果,一个是不取的结果。

最后求答案一定是着来,因为你要的值越大越好。

作者最后发现这道题可以过去,因为数组可以“卡入膏霜”。。。 \downarrow (看下图 + 代码)
分蛋糕暴力解法

#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

所以,做空间优化还是非常非常重要的。。。

最后,思路说清楚了,大家也一定非常明白了吧。

(进入下一节)

多重背包(基础 -> 优化)

截我回目录

未完待续

完全背包(基础 -> 优化)

截我回目录

未完待续

猜你喜欢

转载自blog.csdn.net/yida_allen/article/details/106875773