AcWing 4081. 选数

选数

题意

给定 \(n\) 个整数 \(a_1, a_2, \ldots a_n\)

请你从中选去 恰好 \(k\) 个数字,要求选出来的数字的乘积的末尾的 \(0\) 尽可能多。

请输出末尾 \(0\) 的最大可能数量。

\(1 \le n \le 200, 1 \le k \le n, 1 \le a_i \le 10^{18}\)

分析

对于所有的质因数,只有 \(2 \times 5\) 才会产生末尾 \(0\)

选择 \(k\) 个数字,假设乘积有 \(a\)\(2\)\(b\)\(5\) 。那么产生的末尾 \(0\) 一共 \(min(a, b)\) 个。

所以要求 \(min(a, b)\) 的最大值。

\(f(i, j, k)\) 表示前 \(i\) 个数字,选择 \(j\) 个数字,并且 \(5\) 的个数为 \(k\) 时,\(2\) 的最大数量。

转移方程:\(f(i, j, k) = max(f(i, j, k), f(i-1, j-1, k-c_5) + c_2)\)

其中 \(c_5\) 表示第 \(i\)\(5\) 的数量,\(c_2\) 表示第 \(i\)\(2\) 的数量。

看作二维的 \(01\) 背包问题,\(c_5\) 为体积, \(c_2\) 为价值。

由于只用到了前一层,可以把第一维压掉。

Code

#include <iostream>
#include <cstring>
using namespace std;
const int N = 210;

int f[N][30 * N + 10];

signed main ()
{
    int n, k; cin >> n >> k;
    memset(f, -0x3f, sizeof f);
    f[0][0] = 0;
    for (int i = 1; i <= n; i ++ )
    {
        long long x; cin >> x;
        int c2 = 0, c5 = 0;
        while(x % 2 == 0) { ++ c2; x /= 2; }
        while(x % 5 == 0) { ++ c5; x /= 5; }
        // 体积为c5,价值为c2
        for (int j = k; j >= 1; j -- ) // 选j个数字
            for (int r = j * 25; r >= c5; r -- )
                f[j][r] = max(f[j][r], f[j-1][r - c5] + c2);
    }
    int ans = 0;
    for (int i = 0; i <= 25 * N; i ++ ) ans = max(ans, min(i, f[k][i]));
    cout << ans << endl;
    return 0;
}
posted @ 2021-11-29 11:56  Horb7  阅读(24)  评论(0编辑  收藏  举报