1. 问题描述:
给定 n 个整数 a1,a2,…,an。请你从中选取恰好 k 个数,要求选出的数的乘积的末尾 0 的数量尽可能多。请输出末尾 0 的最大可能数量。
输入格式
第一行包含两个整数 n,k。第二行包含 n 个整数 a1,a2,…,an。
输出格式
一个整数,表示末尾 0 的最大可能数量。
数据范围
前 6 个测试点满足 1 ≤ n,k ≤ 10。
所有测试点满足 1 ≤ n ≤ 200,1 ≤ k ≤ n,1 ≤ ai ≤ 10 ^ 18。
输入样例1:
3 2
50 4 20
输出样例1:
3
输入样例2:
5 3
15 16 3 25 9
输出样例2:
3
输入样例3:
3 3
9 77 13
输出样例3:
0
来源:https://www.acwing.com/problem/content/description/4084/
2. 思路分析:
由题目可知这道题目属于经典的有限制的选择问题,这种问题有另外一个名字叫做背包问题,这两个问题是等价的,一般是从集合中选择一个子集,这个子集需要满足某些条件最终需要使得子集的分值或者求解有多少种的选法;因为是背包模型所以我们需要将题目的各种限制放到状态中,我们可以将选择k个数看成是背包的容量,因为最终需要使得0的数量最多,所以需要尽量包含因为2和因子5,并且因子2和因子5的数量独立的,所以可以将因子2的数量或者因子5的数量放到状态中,这里将因子2或者因子5数量看成是重量的限制,因为需要将其放到状态中所以最终因子2或者因子5的数量需要计算到时间和空间复杂度内的,因子5的数量肯定小于因子2的数量的,ai最大是10 ^ 18,所以因子2的数量最多是60个,因子5的数量最多有25个,所以将来因子5的数量放到状态中,这里将因子5的数量看成重量的限制,这样就可以将其转化为二维费用的零一背包问题,其中dp[i][j]表示恰好选择i个数中有j个5的最多的因子2的数目,使用两维费用的零一背包问题的模板即可,因为最多有200个数每一个数最多有25个因子5所以第二维的长度为5010,因为题目中规定"恰好"选择k个数,对于"恰好"的背包问题,与"最多"...的区别主要是在初始化dp数组初始值不一样,对于"恰好"的背包问题,第一个状态是合法的,其余状态都是不合法的,也即f(0,0) = 0,最后枚举一下恰好选择k个数中1个5,2个5...中2的数目的最大值即可。
3. 代码如下:
class Solution:
def process(self):
M = 5010
n, m = map(int, input().split())
a = [0] + list(map(int, input().split()))
# v[i]表示第i个数因子2的数量, w[i]表示第i个数因子5的数量
v, w = [0] * (n + 10), [0] * (n + 10)
INF = 10 ** 10
# dp[i][j]表示前i个数有j个5的最多的2的数目
dp = [[-INF] * M for i in range(m + 10)]
for i in range(1, n + 1):
x = a[i]
# 计算x中因子2和因子5的数量
while x % 2 == 0:
v[i] += 1
x //= 2
while x % 5 == 0:
w[i] += 1
x //= 5
# 因为是恰好初始化的时候第一个状态是合法的其余状态都是不合法的
dp[0][0] = 0
# 逆序枚举这样可以压缩一维, 类似于之前背包问题的优化
for i in range(1, n + 1):
for j in range(m, 0, -1):
# 下面不是从M枚举到w[i]而且从25 * i枚举到i, 其实是常数优化
for k in range(25 * i, w[i] - 1, -1):
dp[j][k] = max(dp[j][k], dp[j - 1][k - w[i]] + v[i])
res = 0
for i in range(1, M):
# 枚举当前有m个数字有i个5的时候dp[m][2]个2的时候的最小值
res = max(res, min(i, dp[m][i]))
return res
if __name__ == '__main__':
print(Solution().process())