回顾一下 01 背包问题 (每个物品只能选一次)
集合划分的依据:用“最后一步”来划分
完全背包问题:每个物品可以选 0,1,2,3... 个
通过比较可以得到:
背包问题的注意事项
- 当空间优化到1维之后,只有完全背包问题的体积是从小到大循环的
- for 物品
for 体积
for 决策
AcWing 6. 多重背包问题 III
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20010;
int n, m;
int f[N], g[N], q[N];
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ )
{
int v, w, s;
cin >> v >> w >> s;
memcpy(g, f, sizeof f);
for (int j = 0; j < v; j ++ )
{
int hh = 0, tt = -1;
for (int k = j; k <= m; k += v)
{
if (hh <= tt && q[hh] < k - s * v) hh ++ ;
while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ;
q[ ++ tt] = k;
f[k] = g[q[hh]] + (k - q[hh]) / v * w;
}
}
}
cout << f[m] << endl;
return 0;
}
AcWing 423. 采药
这是01背包问题的简单应用
#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
int f[N];
int main()
{
cin >> m >> n;
for(int i = 0; i < n; i ++ )
{
int v, w;
cin >> v >> w;
for(int j = m; j >= v; j -- ) f[j] = max(f[j], f[j - v] + w);
}
cout << f[m] << endl;
return 0;
}
AcWing 1024. 装箱问题
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200010;
int m, n;
int f[N];
int main()
{
cin >> m >> n;
for(int i = 0; i < n; i ++ )
{
int v;
cin >> v;
for(int j = m; j >= v; j --) f[j] = max(f[j], f[j - v] + v);
}
cout << m - f[m] << endl;
return 0;
}
AcWing 1022. 宠物小精灵之收服
- 花费1:精灵球的数量
- 花费2:皮卡丘的体力值
- 价值:小精灵的数量
状态计算:
最多收服的小精灵数量:
最小消耗的体力:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, M = 510;
int n, V1, V2;
int f[N][M];
int main()
{
cin >> V1 >> V2 >> n;
for(int i = 0; i < n; i ++ )
{
int v1, v2;
cin >> v1 >> v2;
for(int j = V1; j >= v1; j -- )
for(int k = V2; k >= v2; k --)
f[j][k] = max(f[j][k], f[j - v1][k - v2] + 1);
}
cout << f[V1][V2 - 1] << ' ';
int k = V2 - 1;
while(k > 0 && f[V1][k - 1] == f[V1][V2 - 1]) k --;
cout << V2 - k << endl;
return 0;
}