马
马
题意
有 \(n\) 个数,初始全为 \(1\)。有 \(3\) 种操作:
- 将任意一个数 \(a \leftarrow a+50\);
- 将任意一个数 \(a \leftarrow a + 20\);
- 将任意一个数 \(a \leftarrow a \times 2\)。
给出每种操作的个数,你可以以任意顺序执行任意操作。
求最多执行多少个操作,使得操作结束后任意 \(a_i \le 100\)。
思路
定义 \(dp_{i,j,k,l}\) 表示考虑前 \(i\) 个数,执行了 \(j\) 次 \(1\) 操作,\(k\) 次 \(2\) 操作,\(l\) 次 \(3\) 操作,\(a_i\) 的最小值。
状态转移方程(类似于完全背包的转移):
\[dp_{i,j,k,l} = \min \left \{ dp_{i,j-1,k,l}+50,dp_{i,j,k-1,l}+20,dp_{i,j,k,l-1}\times 2\} \right.
\]
\[dp_{i,j,k,l}=1 (dp_{i-1,j,k,l}\le100)
\]
为确保不会溢出,转移时可以先判断是否 \(\le 100\) 再转移。
状态数:\(O(nm^3)\),转移:\(O(1)\),总时间复杂度:\(O(nm^3)\)。
空间可以使用滚动数组优化,\(O(m^3)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
int n, m, ans;
int a, b, c;
int dp[2][301][301][301];
bool ok[2][301][301][301];
int main() {
freopen("horse.in", "r", stdin);
freopen("horse.out", "w", stdout);
cin >> n >> m;
cin >> a >> b >> c;
dp[0][0][0][0] = 1, ok[0][0][0][0] = 1;
for (int i = 1, p = 1; i <= n; i ++, p ^= 1) {
for (int j = 0; j <= a; j ++) {
for (int k = 0; k <= b; k ++) {
for (int l = 0; l <= c; l ++) {
dp[p][j][k][l] = 1e9, ok[p][j][k][l] = 0;
if (ok[p ^ 1][j][k][l]) dp[p][j][k][l] = 1;
if (j && ok[p][j - 1][k][l]) dp[p][j][k][l] = min(dp[p][j][k][l], dp[p][j - 1][k][l] + 50);
if (k && ok[p][j][k - 1][l]) dp[p][j][k][l] = min(dp[p][j][k][l], dp[p][j][k - 1][l] + 20);
if (l && ok[p][j][k][l - 1]) dp[p][j][k][l] = min(dp[p][j][k][l], dp[p][j][k][l - 1] * 2);
ok[p][j][k][l] = (dp[p][j][k][l] <= 100);
if (ok[p][j][k][l]) ans = max(ans, j + k + l);
}
}
}
}
cout << ans << "\n";
return 0;
}
本文来自博客园,作者:maniubi,转载请注明原文链接:https://www.cnblogs.com/maniubi/p/18548136,orz