AcWing 4081. 选数(二维费用背包问题)
题意
有n个数,现在需要选出k个。让这k个数相乘后末尾零的个数最多
算法
动态规划
尾零的数量等价于选出的 \(k\) 个数的质因子分解中的 2 的幂次与 5 的幂次。
状态定义:
\(f[i][j][k]\) 表示在前 \(i\) 个数中选 \(j\) 个数,且 5 的因子数量为 \(k\) 时 2的因子数量的最大
状态计算:
不选第i个:\(f[i][j][k] = f[i - 1][j][k]\)
选第i个:\(f[i][j][k] = max(f[i][j][k], f[i - 1][j - cnt_5] + cnt_2\)
发现问题便是01背包问题,可优逆序处理化掉一维度。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 210, M = 5010;
int n, m;
int v[N], w[N];
int f[N][M];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
{
LL x;
cin >> x;
while (x % 5 == 0) x /= 5, v[i] ++ ;
while (x % 2 == 0) x /= 2, w[i] ++ ;
}
memset(f, -0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i ++ )
for (int j = m; j; j -- )
for (int k = i * 25; k >= v[i]; k -- )
f[j][k] = max(f[j][k], f[j - 1][k - v[i]] + w[i]);
int res = 0;
for (int i = 1; i < M; i ++ )
res = max(res, min(i, f[m][i]));
cout << res << endl;
return 0;
}