0、总览
1、多重背包问题(单调队列优化)
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20010;
int n, m;
//q队列 记录的是下标
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)
{
//队头<左端点 出队 队头元素滑出滑动窗口
//假设s=3 k=r+3v 滑动窗口是 [r,r+v,r+2v] 此时求的是f[r+3v]
//求f[r+3v]需要得到r,r+v,r+2v时的最大值 则hh不需要抛出
//假设q[hh]=r;
//k=r+3v k-3v=r q[hh]=r r q[hh]则不弹出
if (hh <= tt && q[hh] < k - s * v) hh ++ ;
//删除队尾元素
//j是余数 g[q[tt]]队尾元素 g[k]新插入的元素
//q[tt]-j/v*w 队尾元素减去几个w
//(k-j)/v*w 新元素 减去几个w
//最大值 递减数列 <=
//很难算出w的具体数值,但是知道从r,r+v->j-V->j 每个之间相差w
//r-0w,r+v-1w,利用相对数据找到最大值(此处的最大值是f[i-1,(j-xv)])
//没加偏移量
//假设s=3 k=r+3v 滑动窗口是 [r,r+v,r+2v] 此时求的是f[r+3v]
//求f[r+3v]需要得到r,r+v,r+2v 还有f[i-1,r+3v]时的最大值
//在这种情况下,g[k]可以看作f[i-1,j](便于理解f[i-1,j] f[i-1,j-v])。
while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ;
q[ ++ tt] = k;
//q[hh] 最大值的下标
//g[q[hh]] f值 上一层的f+?w ?=队头元素距离距离数值 含有多少个w
//此处的最大值是f[i-1,(j-xv)])没加偏移量。
//对相对数值 需要加上偏移量才能变成f[k]
f[k] = g[q[hh]] + (k - q[hh]) / v * w;
}
}
}
cout << f[m] << endl;
return 0;
}
2、采药(01)
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int m, n;
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;
}
3、装箱问题(01)
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 20010;
int n, m;
int f[M];
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;
}
4、宠物小精灵之收服(二维费用01)
#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 -- )
//因为k是消耗的体力值,体力值最大为100,消耗的体力值为100-1;
//体力值为0就结束,固然消耗的体力值要比最大体力值小1
//V2-1 皮卡丘的体积不为0
for (int k = V2 - 1; k >= v2; k -- )
f[j][k] = max(f[j][k], f[j - v1][k - v2] + 1);
}
//消耗的精灵球是V1,消耗的体力值是V2-1,时的捕捉小精灵最大数量
cout << f[V1][V2 - 1] << ' ';
//k是消耗的体力值,因此k比最大体力值小1
int k = V2 - 1;
while (k > 0 && f[V1][k - 1] == f[V1][V2 - 1]) k -- ;
//V2-k 剩余的体力值 最大体力值-消耗体力值
cout << V2 - k << endl;
return 0;
}