题意

\(n\) 个数,初始全为 \(1\)。有 \(3\) 种操作:

  1. 将任意一个数 \(a \leftarrow a+50\)
  2. 将任意一个数 \(a \leftarrow a + 20\)
  3. 将任意一个数 \(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;
}
posted @ 2024-11-15 16:04  maniubi  阅读(2)  评论(0编辑  收藏  举报