题目:
链接:https://ac.nowcoder.com/acm/problem/26157
给出 n 个数字,第 i 个数字为 a[i],我们从中选出 k 个数字,使得乘积后缀 0 的个数最多。
思路 :
先把每个数拆分成有多少2和5
然后考虑对这些数跑01背包,每个数里5的个数是花费,2的个数是重量
接下来考虑方程的转移
动态规划 dp[i][j][s]表示前i个数里面拿j个,一共有s个5的时候最多有多少个2
然后考虑dp的转移
当前到第i个数
先考虑不拿这个数
那么就有dp[i][j][s]=dp[i-1][j][s];
然后考虑拿当前这个数
有 dp[i][j][s]=max(dp[i][j][s],dp[i-1][j-1][s-c5[i]]+c2[i]);
最后要注意的初始条件
dp[0][0][0]=0;
dp[1][0][0]=0;
这两个初始条件都不能缺少,否则会错
最后对三维dp改滚动数组
代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4 + 10;
int c2[222];
int c5[222];
int dp[2][222][maxn];
int sum=0;
int main()
{
int n,k;
ll x;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>x;
while(x%5==0)x/=5,c5[i]++;
sum+=c5[i];
while(x%2==0)x/=2,c2[i]++;
//printf("%d %d\n",c2[i],c5[i]);
}
memset(dp,0xc0,sizeof(dp));
dp[0][0][0]=0;
dp[1][0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=min(i,k);j++)
{
for(int s=0;s<=sum;s++)
{
dp[i&1][j][s]=dp[(i-1)&1][j][s];
if(s>=c5[i])dp[i&1][j][s]=max(dp[i&1][j][s],dp[(i-1)&1][j-1][s-c5[i]]+c2[i]);
}
}
}
int ans=0;
for(int i=0;i<=sum;i++)
{
ans=max(ans,min(i,dp[n&1][k][i]));
}
cout<<ans;
return 0;
}
/*
4 3
2 5 5 2
*/