装箱问题【暴力+二进制+记忆化搜索+dp,从小白->进阶】

题目:

有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数)。
要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

思路:

搜索:

递归的参数:

  1. int u : 表示第u件物品;
  2. int sum:表示当前所选物品的总体积;

递归的出口:

  1. if (sum > m):如果总体积大于背包体积了,就不合法。
  2. if (u > n):表示n件物品都已经枚举完毕了,判断最值。

递归体:
dfs (u+1,sum):表示不选第u件物品,所以sum不累加它;
dfs(u+1,sum+w[u]):表示选第u件物品,所以sum累加第u件物品的体积。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 40, M = 2e4 + 10;
int f[N][M];
int w[N];
int n, m, res;

void dfs (int u, int sum)
{
    if (sum > m)    //剪枝优化,不正确的解!
        return ;
    
    if (u > n)
    {
        res = max(res, sum);
        return ;
    }
    
    dfs (u+1, sum);     //第u件物品不选!
    dfs (u+1, sum+w[u]);    //选第u件物品!
}

int main()
{
    cin >> m >> n;  //体积和数量!
    
    for (int i=1; i <= n; i ++)
        cin >> w[i];
    
    dfs (1, 0);
    cout << m - res << endl;
    return 0;
}
二进制枚举:
  1. 利用二进制进行状态的表示,因为一共n件物品,每件物品有选和不选两种状态,所以一共有2n-1种选法;
  2. 我们需要枚举每一种选法,从而进行累加每种选法的物品体积,求取合法状态下的最大体积。
  3. 比如:n = 7 = 1011;表示第4,2,1件物品选!累加体积。

其中,i的二进制表示的第j位为1表示选择了第j个物品,为0表示没有选择。在遍历所有状态时,我们可以使用一个整数i来表示当前状态,然后枚举i的二进制表示的每一位,判断这一位是否为1,如果是则将对应物品的体积加入总体积sum中。
最后,如果当前状态的总体积小于等于箱子容量V。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 40;
int m, n, res;
int w[N];

int main()
{
    cin >> m >> n;
    
    for (int i=0; i < n; i ++)
        cin >> w[i];
    
    for (int i=0; i < 1<<n; i ++)   //枚举每一种选法
    {
        int sum = 0;
        for (int j=0; j < n; j ++)  //判断每件物品选还是不选
            if (i >> j & 1)
                sum += w[j];
        if (sum <= m){
            res = max(sum, res);
        }
    }
    cout << m - res << endl;
    return 0;
}
dp:

剩余空间最小 == 从n个物品里面选,总体积不超过 V的前提下,所选物品的总体积尽可能大的所有选法。然后最。后利用 容量 V - 所选物品的总体积 = 剩余最小空间。

朴素版本:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 40, M = 2e4 + 10;
int f[N][M];    //表示从前i件物品里面选,且总体积不超过j的所有选法:
int m, n;  
int w[N];

int main()
{
    cin >> m >> n;  //体积,数量
    for (int i=1; i <= n; i ++)
        cin >> w[i];
    
    for (int i=1; i <= n; i ++)  //从前i件物品里面选
    {
        for (int j=0; j <= m; j ++) //分别记录背包体积为1,2,3...m不同情况下的最大价值。
        {
            f[i][j] = f[i-1][j];
            if (j >= w[i])
                f[i][j] = max(f[i][j], f[i-1][j-w[i]] + w[i]);
        }
    }
    
    cout << m - f[n][m];
    return 0;
}

滚动优化:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int M = 2e4 + 10, N = 40;
int n, m;
int f[M];   //从前i件物品里面选,总体积不超过j的选法。属性max值!
int w[N];

int main()
{
    cin >> m >> n;  //分别表示背包体积,物品数量!
    
    for (int i=0; i < n;  i++)
        cin >> w[i];
    
    for (int i=0; i < n; i ++)
    {
        for (int j=m; j >= w[i]; j --)
        {
            f[j] = max(f[j], f[j-w[i]] + w[i]);
        }
    }
    cout << m - f[m] << endl;
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值