一、先温习一下01背包问题
有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
条件汇总
--------
背包限制容量:Z
此时背包容量:C
物品:1 , i ... n //代表编号
重量:wight[1] , wight[i] ... wight[n]
价值:value[1] , value[i] ... value[n]
目标:在wight总和小于Z的情况下,使得value的总和最大
记录已计算好的结果用数组dp表示
dp[i][j]的含义是:从下标为[0至i]的物品⾥任意取,放进容量为j的背包,价值总和最⼤是多少(一个数字)。
从这个题目中可以看出,01背包的特点就是:每种物品仅有一件,可以选择放或不放。
- 确定最优子结构(不用重复的东西确定是没问题的)
- 状态转移方程(更好的结果就更新)
01背包问题的状态转移方程:
dp[i][wight] = max{
dp[i-1][C] ,
dp[i-1][C-wight[i]]+value[i]
}
dp[i-1][C]代表的就是不将这件物品放入背包后的总价值,
dp[i-1][C-wight[i]]+value[i]则是代表将第i件放入背包之后的总价值
然后就是遍历执行比对,以数组f存储空间换取执行时间速度避免重复。
for (i = 1; i <= n; i++)
for (j = v; j >= wight[i]; j--)//在这里,背包放入物品后,容量不断的减少,直到再也放不进了
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - wight[i]] + value[i]);
执行完了一个如同九九乘法表的存储最优配置的数组就生成了,答案靠查表(让C==Z时的那一列的最后一行的值)就行了~
二、动态规划策略
在搜索中会重复计算一些结点,所以,如果我们把搜索过程中计算过的结点的值记录下来(前提是最优子结构)以保证不重复计算的话,速度就会提高很多。
三、题解
这个题倒着推满足条件时的最大价值时多少,在推的过程中记录,就可以省去生成表格后再找的麻烦。
#include <iostream>
#include <cstring>
#include <algorithm>//min函数
using namespace std;
const int N = 35, M = 3e5 + 10;
int f[N][M];
int a[N];
int main()
{
int n, x;
cin >> n >> x;
int sum = 0;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
sum += a[i];//累加总价值
}
for (int i = 1; i <= n; i ++) f[i][sum] = sum;//最后一列,价值给满
for (int j = x; j <= sum; j ++) f[0][j] = sum;//第一行,价值给满
//逆着求动态规划表
int res = 3000010;
for (int i = 1; i <= n; i ++)
{
for (int j = sum; j >= x; j --)//遍历试着往下减
{
f[i][j] = f[i - 1][j];
if (j + a[i] <= sum)//价值更小的话,就更新
f[i][j] = min(f[i][j], f[i - 1][j + a[i]] - a[i]);
if (f[i][j] >= x) res = min(res, f[i][j]);//小于x包邮的情况下,找到更小的花费(因为达到后就不再更新res了)
}
}
cout << res;
system("pause");
return 0;
}
代码参考:202209-2 CCF 何以包邮? (01背包解法(两种解法) + 详细思路)_一只可爱的小猴子的博客-CSDN博客