Codeforces837D RoundSubset

首先在下参考了评测#29184017
以及http://blog.csdn.net/my_sunshine26/article/details/76652764
我天真的以为这是一个区间dp……没想到竟然是埋藏这么深的一道背包。是在下输了。


来说说题意。有n个数,其中选k个,要让我们输出这k个数之积的最多的0的个数。比如50 4 20里选俩,50*4=200有两个0,4*20=80有1个0,50*20=1000有3个0,所以输出3.
首先要思考,如何能凑出来一个0?要么是0与一个数相乘,要么就是5与2相乘。前者如果是0需要特殊考虑,否则任何的0都是可以分解为5与2的。所以我们可以首先处理出来一个数的5、2的因数个数。
回顾一下0-1背包的dp转移方程:
f[j]=max(f[j],f[j-w[i]]+p[i])
现在我们不妨从另一个角度来思考这个问题:如果用f[i][j]表示以i为开始的j个因数2能达到的最多的因数5的值,那么:
f[i][j]=max(f[i][j],f[i-1][j-pr2[k]]+pr5[i])
还有一点要注意,在循环遍历dp数组的时候,我们应该求的是因数2与因数5的最小值。
实现:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define MAX 203*60
//如果每个数都有最多2的因子,则共有203*log10^18约为60个2的因子
struct num{
    LL n;
    int n2,n5;
}w[203];
LL dp[203][MAX],n,k;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>k;
    for(int i=1;i<=n;++i){
        cin>>w[i].n;
        while(w[i].n%2==0) ++w[i].n2,w[i].n>>=1;//枚举因数2
        while(w[i].n%5==0) ++w[i].n5,w[i].n/=5;//枚举因数5
    }
    for(int i=0;i<n;++i)
        for(int j=0;j<MAX;++j)
            dp[i][j]=INT_MIN;
    dp[0][0]=0;
    for(int i=1;i<=n;++i){//当前选择的数i
        for(int j=k;j>=1;--j){//选择的第j个数
            for(int l=MAX;l>=0;--l){//因数2的个数
                if(l-w[i].n2>=0){
                    dp[j][l]=max(dp[j][l],dp[j-1][l-w[i].n2]+w[i].n5);
                }
            }
        }
    }
    LL ans=0;
    for(LL j=0;j<MAX;++j)//枚举求出要求的结果
        ans=max(ans,min(j,dp[k][j]));
    cout<<ans;
}

我真是太菜了…感觉NOIP药丸

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值