题目链接 http://acm.sdut.edu.cn/onlinejudge3/contests/4098/problems/P
题面
该测试具体描述如下:
规定一个数字末尾 00 的个数即是这个数的希望值。
每次测试中,刘老师将分发给每人一个有 nn 个数的数组,你需要恰好选择 kk 个数字(每个数只能选一次),并将这 kk 个数相乘得到一个数字 uu
数字 uu 的希望值即为每个人在测试得到的最终希望值。
如果你可以在每次测试中均取得最大的希望值,刘老师将送给你一个 Accepted
如果你能 AC 这道题目,你将获得“ACM 的希望”荣誉称号。
Inut
第一行包含两个整数 n 和 k (1≤n≤200,1≤k≤n)
第二行包含 n 个以空格分隔的整数 代表n个数字
Output
输出一个整数,表示你在测试过程中能获得的最大希望值。
Sample
Input
3 2
50 4 20
5 3
15 16 3 25 9
Output
3
3
解析
背包问题变式,可以先从三维的角度考虑一下问题。
• 首先 2 与 5 乘积才可以得出末尾 0
• 记录每个数能分解出的 2 和 5 的数目, 然后 01 背包即可。
• 用 dpi,j,m 表示从前 i 个数中选出 j 个数其中 5 的个数为 m 个的情况下 2 的数目最多是多少。
• 初始状态为 dp0,0,0 = 0 , 推到终态 dpn,k,x 即可。注意要存在上一种状态才能到达下一种状态。
• 开三维可能会 MLE, 可以优化一下空间, 去掉 i 这维就变成了一个二维背包问题。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2100;
const int MAXM = 6010;
int n, k, a, dp[MAXN][MAXM], cnt2[MAXN], cnt5[MAXN];
void work() {
scanf("%lld%lld", &n, &k);
int mx = 0;
for(int i = 1; i <= n; ++i) {
int a;
scanf("%lld", &a);
while(a % 5 == 0) ++cnt5[i], a /= 5;
mx += cnt5[i];
while(a % 2 == 0) ++cnt2[i], a /= 2;
}
memset(dp, -0x3f, sizeof(dp));
dp[0][0] = 0;
int ans = 0;
for(int i = 1; i <= n; ++i) {
for(int j = min(i, k); j >= 1; j--)
for(int l = cnt5[i]; l <= mx; ++l) {
dp[j][l] = max(dp[j][l], dp[j - 1][l - cnt5[i]] + cnt2[i]);
if(j == k) ans = max(ans, min(l, dp[j][l]));
}
}
printf("%lld\n", ans);
}
signed main() {
int t = 1;
// scanf("%d", &_);
while(t--) work();
return 0;
}