题目:
有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数)。
要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
思路:
搜索:
递归的参数:
- int u : 表示第u件物品;
- int sum:表示当前所选物品的总体积;
递归的出口:
- if (sum > m):如果总体积大于背包体积了,就不合法。
- 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;
}
二进制枚举:
- 利用二进制进行状态的表示,因为一共n件物品,每件物品有选和不选两种状态,所以一共有2n-1种选法;
- 我们需要枚举每一种选法,从而进行累加每种选法的物品体积,求取合法状态下的最大体积。
- 比如: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;
}