题目 | Nazrin the Greeeeeedy Mouse
2023牛客暑期多校训练营5
H - Nazrin the Greeeeeedy Mouse
https://ac.nowcoder.com/acm/contest/57359/H
题意
有 $n$ 个物品,第 $i$ 个物品的体积是 $a_i$,价值是 $b_i$,有 $m$ 个背包,第 $i$ 个背包的容积是 $s_i$ 且 $s_{i-1}\leq s_i$.
物品需要从 $1$ 到 $n$ 顺序取,即取了第 $i$ 个物品后,$<i$ 的没有取的物品不能再取。
背包需要从 $1$ 到 $m$ 顺序用,即开始用第 $i$ 个背包后,$<i$ 的背包就不能再次使用了。
$1\leq n\leq 200,\;1\leq m\leq 10^5$
$1\leq a_i\leq 200,\; 1\leq b_i\leq 10^5,\; 1\leq s_i\leq 200$
题解
当 $m>n$ 时,只考虑最大的 $n$ 个背包即可。
因为对于 $n$ 个物品,最多用掉 $n$ 个背包(每个背包装 $1$ 个),又因为背包大小单调不减 $s_{i-1}\leq s_i$,那么如果 $m>n$,取最靠后的最大的 $n$ 个背包一定是最优的方案。
预处理每个区间的 0/1 背包问题
对于每个区间 $[i,j]$,用 0/1 背包模型算出背包大小为 $k$ 时能取到的最大的价值。实际上就是固定起点为 $i$,跑一遍 $i\sim n$ 的 0/1 背包问题。代码中的 $f_{i,j,k}:=$ 起点为 $i$ 终点为 $j$ 的区间,背包大小为 $k$ 时能取到的最大的价值。
预处理的时间复杂度为:$O(n^2\max\{s_i\})$
计算最终结果
$dp_{i,k}:=$ 第 $1\sim i$ 个背包取区间 $[1,k]$ 的物品,能取到的最大价值。
转移方式为:$dp_{i,k}=dp_{i-1,j}+f_{j,k,w_i}$
计算结果的时间复杂度为:$O(n^3)$
如果没有发现可只考虑 $n$ 个最大的背包,时间复杂度是 $O(n^2m)$,肯定是过不去的。
代码
#include <bits/stdc++.h>
#define endl '\n'
// #define int long long
using namespace std;
constexpr int MAXN = 210;
int f[MAXN][MAXN][MAXN];
int a[MAXN], b[MAXN], s[MAXN];
int dp[MAXN][2];
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i];
for (int i = 1; i <= m; i++)
cin >> s[max(1, min(0, n - m) + i)];
m = min(m, n);
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
for (int k = 0; k <= 200; k++)
{
f[i][j][k] = f[i][j - 1][k];
if (k - a[j] >= 0)
f[i][j][k] = max(f[i][j][k], f[i][j - 1][k - a[j]] + b[j]);
}
}
}
int p = 0;
for (int i = 1; i <= m; i++)
{
for (int j = 0; j <= 200; j++)
dp[j][p] = 0;
for (int j = 0; j <= n; j++)
for (int k = j + 1; k <= n; k++)
dp[k][p] = max(dp[k][p], dp[j][p ^ 1] + f[j + 1][k][s[i]]);
p ^= 1;
}
cout << dp[n][p ^ 1] << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi