零、前导知识
0x3f3f3f3f
数值大于1e9
,且满足无穷大 + 无穷大 = 无穷大(不会溢出int
)
memset(f, 0xcf, sizeof f) -> -808464433
memset(f, -0x3f, sizeof f) -> -1044266559
memset(f, 0x3f, sizeof f) -> 1061109567
对于一维背包问题,注意区分该问题的下面这三种情况,即至多/恰好/至少,它们的状态转移方程其实是一样的,差异在于初始化。
一、体积至多是v时的最小/大值
这是背包问题中最常见的问法,取最小用\(min\),取最大用\(max\)。
方法:全部初始化为\(0\),且保证\(v\)大于等于\(0\)。
代表题目:\(AcWing\) \(423\) .采药,其代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main() {
//优化输入
ios::sync_with_stdio(false);
//物品个数n
cin >> m >> n;
//读入体积和重量
for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
//01背包模板
for (int i = 1; i <= n; i++)
for (int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
二、 体积恰好是v时的最小/大值
方法:\(f[0]\)初始化为\(0\),其余初始化为正/负无穷(分别对应题目问的最小/最大,代表不合法状态,不应该被选取),且保证\(v\)大于等于\(0\)。
代表题目:\(AcWing\) \(734\) 能量石,其代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 110; //能量石个数上限
const int M = 10010;//
//用来描述每个能量石的结构体
struct Node {
int s; //吃掉这块能量石需要花费的时间为s秒
int e; //可以获利e个能量
int l; //不吃的话,每秒失去l个能量
} q[N]; //能量石的数组,其实这里开的数组开的特别大了,题目中说是110就够了。
//结构体对比函数
bool cmp(const Node &a, const Node &b) {
return a.s * b.l < b.s * a.l;
}
int n; //能量石的数量
int f[M]; //f[i]:花i个时间得到的最大能量
int idx; //输出是第几轮的测试数据
int main() {
//优化输入
ios::sync_with_stdio(false);
//T组测试数据
int T;
cin >> T;
while (T--) {
//初始化为负无穷 -0x3f---> -1044266559
memset(f, -0x3f, sizeof f);
cin >> n;
//总时长,背包容量
int m = 0;
//读入数据
for (int i = 1; i <= n; i++) {
cin >> q[i].s >> q[i].e >> q[i].l;
m += q[i].s;
}
//贪心排序
sort(q + 1, q + 1 + n, cmp);
//01背包
f[0] = 0;
for (int i = 1; i <= n; i++)
for (int j = m; j >= q[i].s; j--)
f[j] = max(f[j], f[j - q[i].s] + max(0, q[i].e - (j - q[i].s) * q[i].l));
//找出最大值
int res = 0;
for (int i = 1; i <= m; i++) res = max(res, f[i]);
printf("Case #%d: %d\n", ++idx, res);
}
return 0;
}
三、 体积至少是v时的最小/大值
方法:\(f[0]\)初始化为\(0\),其余初始化为正/负无穷(分别对应题目问的最小/最大,代表不合法状态,不应该被选取),无需保证\(v\)大于等于\(0\)。
代表题目:\(AcWing\) \(1020\).潜水员,其代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010; //气缸的个数上限
const int M = 100; //需要的氧气、氮气上限值
int V1; //氧需要的量
int V2; //氮需要的量
int n; //气缸的个数
int v1[N], v2[N], w[N];//第i个气缸里的氧和氮的容量及气缸重量
int f[M][M]; //DP数组
int main() {
//读入氧需要的量,氮需要的量,气缸的个数
cin >> V1 >> V2 >> n;
//读入每个气缸的氧和氮的容量及气缸重量
for (int i = 1; i <= n; i++) cin >> v1[i] >> v2[i] >> w[i];
//求最小值要把除初始状态以外的所有状态初始化为+∞
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
//因为采用了降维,所以体积都是倒着的~
for (int i = 1; i <= n; i++)
for (int j = V1; j >= 0; j--)
for (int k = V2; k >= 0; k--)
f[j][k] = min(f[j][k], f[max(j - v1[i], 0)][max(k - v2[i], 0)] + w[i]);
//输出
printf("%d", f[V1][V2]);
return 0;
}