01背包
P1049 装箱问题
f[i][j]: 把前面 i 件物品放进容量为 j 的背包当中所获得的最大价值
现在有 n 件物品,背包容积为 V
那么列出状态转移方程:
f[i][j]
= max(f[i-1][j], f[i-1][j-v[i]] + w[i]) , j > v[i]
= f[i-1][j] , j <= v[i]
= 0 , i = 0 or j = 0 //全局数组创建的时候默认为 0,可省略
其实就是对任意一个物品 i 而言,要不要放进背包的问题,如果不要,最大价值是 f[i-1][j],否则,可以创造f[i-1][j-v[i]] + w[i] 的最大价值
#include<bits/stdc++.h>//万能头文件
using namespace std;
#define maxn 20005
int f[maxn][maxn],
s[maxn], //时间, 即空间 space
v[maxn]; //价值 value
int main()
{
int V, n, i, j;
scanf("%d%d",&V,&n);
for(i = 1; i <= n; i++)
{
scanf("%d",&v[i]);
s[i] = v[i];
}
for(i = 1; i <= n; i++)
{
for(j = 1; j <= V; j++)
{
f[i][j] = f[i-1][j];//如果放不下物品 i
if(s[i] <= j)
{
f[i][j] = max(f[i-1][j-s[i]]+v[i], f[i-1][j]);
}
}
}
printf("%d", V - f[n][V]);
return 0;
}
主要是把一般的二维转化成一维度的问题。
我想到有2种方式:
(1):令f[j] = f[n-1][j], 之前有 f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + w[i]) , j > v[i],因为f[i][j] >= f[i-1][j],可以多选择一个物品时的最大价值一定不会低,那么 f[j] = f[n-1][j] >= f[i-1][j] , n >= i ,好了原式可化为
f[j] = max(f[j], f[j-v[i]] + w[i])
还要注意一点,要反着算,因为这样的话,在推 f[p] 的时候就有 f[p-v[i]] = f[i-1][p-v[i]],算到这一层(i)的时候 i = 0, 1, … i-1 已经跑完了,不错的,f[p-v[i]]刚好存储了我想要的数据,要是还按照原来的顺序的话,这个时候 f[p-v[i]] 就已经被更新过了,是f[i][p-v[i]] 不是我们需要的,错误。
(2):用人话来说原来的状态转移方程是在考虑要不要放物品 i 的问题,也就是说,和上面一个状态在比较,假如把第一维去掉,是没有影响的(i、i-1 就代表了当前状态和上一个状态,数组的历史数组自然存储了下来)(这是某位大佬同学告诉我的,偷偷告诉你,他高中就学会了)
#include<bits/stdc++.h>//万能头文件
using namespace std;
#define N 1000000
int v[105], s[105];
//v[i]: 第i件物品的价值, s[i]:第i件物品的体积
//这里价值即为体积
int f[N];//只需要一维数组就好了 f[j] = f[n-1][j]
int main()
{
int i, j;
int V, n; // n件物品,背包容量 V
scanf("%d%d", &V, &n);
for(i = 1; i <= n; i++)
{
scanf("%d", &s[i]);
}
for(i = 1; i <= n; i++)
{
for(j = V; j >= s[i]; j--)//反着来
{
f[j] = max(f[j], f[j-s[i]]+s[i]);
}
}
printf("%d", V-f[V]);
return 0;
}