对于要装满的 01 背包
新疆省赛(虚无的后缀)
链接:https://ac.nowcoder.com/acm/contest/15589/H
来源:牛客网
题目描述
给出 n 个数字,第 i 个数字为 a[i],我们从中选出 k 个数字,使得乘积后缀 0 的个数最多。
输入描述:
第一行,两个正整数 n,k(1 \leq k \leq n \leq 200)n,k(1≤k≤n≤200),第 2 行 n 个正整数表示 a_i(a_i \leq 10^{18})a
i
(a
i
≤10
18
)
输出描述:
输出一个整数,表示最多有多少个后缀 0
示例1
输入
2 2
20 5
输出
2
示例2
输入
3 2
2 5 20
输出
2
题解:
有多少个0,即看一个数分解后的质因子中2和5的最小值即可,故
本题可以看做是一个要装满的 01 背包 即可以先将之理解为 dp[i][j][k] 即对于前i个物品,剩余了5的个数时(可以看做01背包的重量)2的最大个数,因为眼严格算出5和2的个数,所以要求5的个数要全部被用完,即要求装满的完全背包,又因为如果直接dp三维数组可能会爆掉,所以可以将其压成一维的形式(注意要从大到小进行,因为大的值会被小的不断更新)于是就可以有如下代码
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int sum2[205];
int sum5[205];
int all;
int dp[205][205*30];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
ll x;
cin>>x;
while(x%2==0) //统计2的个数
{
sum2[i]++;
x/=2;
}
while(x%5==0) //统计5的个数
{
sum5[i]++;
x/=5;
}
all+=sum5[i]; //以5的个数作为背包容量
}
for(int i=0;i<=n;i++)
for(int j=0;j<=all;j++)
dp[i][j]=-2e9; //因为要装满,所以先将所有可能置位负无穷,并保留初始状
dp[0][0]=0; //让数据只能从初始状态转移
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
for(int k=all;k>=sum5[i];k--)
dp[j][k]=max(dp[j-1][k-sum5[i]]+sum2[i],dp[j][k]);//即为前i个物品,选了j个时的 2的最大个数
int ans=0;
for(int i=1;i<=all;i++) ans=max(ans,min(dp[m][i],i)); //答案即为2和5最小值中的最大值
cout<<ans<<endl;
return 0;
}