[01背包变形 二维费用]837 D. Round Subset

[01背包变形]837 D. Round Subset

题目

题目链接

题意
我们把一个数的 roundness 值定义为它末尾 0 的个数。
给你一个长度为 n 的数列,要求你从中选出 k 个数,使得这些选出的数的积的 roundness 值最大。也就是求 m a x { m i n ( ∑ c n t 2 , ∑ c n t 5 ) } max\{min(\sum{cnt_2},\sum{cnt_5})\} max{min(cnt2,cnt5)}

思路

末尾0的个数那就要统计2和5的个数,先预处理每个数能贡献多少2和5
选K个数容易想到01背包,接下来思考动态规划部分
原问题:一个长度为 n 的数列,要求你从中选出 k 个数,使得这些选出的数的积的 roundness 值最大。
子问题:一个长度为i的数列,要求你从中选出 j 个数,使得这些选出的数的积的 roundness 值最大。
尝试列出转移方程,令 f [ i ] [ j ] f[i][j] f[i][j]为答案数组, t w o [ i ] 为 第 i 个 数 因 子 2 数 量 two[i]为第i个数因子2数量 two[i]i2, f i v e [ i ] 为 第 i 个 数 因 子 5 数 量 five[i]为第i个数因子5数量 five[i]i5
不 选 当 前 数 f [ i ] [ j ] = f [ i − 1 ] [ j ] 不选当前数f[i][j]=f[i-1][j] f[i][j]=f[i1][j]
选 当 前 数 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + ( t w o [ i ] , f i v e [ i ] ) 选当前数f[i][j]=f[i-1][j-1]+(two[i],five[i]) f[i][j]=f[i1][j1]+(two[i],five[i])发现写不出来

为什么写不出状态转移方程?
思考一下历史状态信息+当前第i个物品的信息能不能推出新状态的信息
历史信息: f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1]
告诉你状态 ( i , j ) (i,j) (i,j)下, m i n ( c n t 2 , c n t 5 ) = f [ i − 1 ] [ j − 1 ] min(cnt_2,cnt_5)=f[i-1][j-1] min(cnt2,cnt5)=f[i1][j1]
新加入物品信息: t w o [ i ] , f i v e [ i ] two[i],five[i] two[i],five[i]
告诉你新加入物品会使得 c n t 2 + t w o [ i ] cnt_2+two[i] cnt2+two[i]以及 c n t 5 + f i v e [ i ] cnt_5+five[i] cnt5+five[i]
但是我们现在只知道 m i n ( c n t 2 , c n t 5 ) min(cnt_2,cnt_5) min(cnt2,cnt5),我们没法通过新加入物品的信息得到 m i n ( c n t 2 + t w o [ i ] , c n t 5 + f i v e [ i ] ) min(cnt_2+two[i],cnt_5+five[i]) min(cnt2+two[i],cnt5+five[i])
那么这种情况我们要考虑维护新的信息

考虑维护新的信息
如果我们知道每个状态的 c n t 2 和 c n t 5 cnt_2和cnt_5 cnt2cnt5那我们就可以很容易的推出新状态了,我们添加两个维度用来记录 c n t 2 和 c n t 5 cnt_2和cnt_5 cnt2cnt5
可以得到选第i个物品的状态转移
f [ i ] [ j ] [ p ] [ q ] = f [ i ] [ j ] [ p − t w o [ i ] ] [ q − f i v e [ i ] ] + m i n ( p , q ) f[i][j][p][q]=f[i][j][p-two[i]][q-five[i]]+min(p,q) f[i][j][p][q]=f[i][j][ptwo[i]][qfive[i]]+min(p,q)
但是考虑p和q范围,1<=ai<=1e18,单个数two[i]上限为60左右,five[i]上限为26左右,这有200个数,时空双爆炸,螺旋升天。

考虑可不可以把其中一个维度扔出来作为dp的含义
尝试把 c n t 2 cnt_2 cnt2扔出来作为dp的含义
g [ i ] [ j ] [ p ] g[i][j][p] g[i][j][p]表示前i个中选j个且他们的积有p个因子5的情况下,最多可以有多少个2
状态转移方式
不选第i个 g [ i ] [ j ] [ p ] = g [ i − 1 ] [ j ] [ p ] g[i][j][p]=g[i-1][j][p] g[i][j][p]=g[i1][j][p]
选第i个 g [ i ] [ j ] [ p ] = m a x ( g [ i ] [ j ] [ p ] , g [ i − 1 ] [ j − 1 ] [ p − f i v e [ i ] ] + t w o [ i ] ) g[i][j][p]=max(g[i][j][p],g[i-1][j-1][p-five[i]]+two[i]) g[i][j][p]=max(g[i][j][p],g[i1][j1][pfive[i]]+two[i])
初始值:默认为负无穷,g[1][0][0]=0(因为这题恰好)
最后遍历一遍取max{min(p,q)}就行了
进步优化可以压掉第一个维度,类似01背包压掉一维度

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
int n,m,ans,tmp;
int a[210],cnt2[210],cnt5[210];
int dp[210][20000];
//设dp[i][j]表示选i个物品,5有j个时2的最多数量。
signed main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        tmp=a[i];
        while(tmp%2==0)
            cnt2[i]++,tmp/=2;
        while(tmp%5==0)
            cnt5[i]++,tmp/=5;
    }
    memset(dp,0xcf,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=1;j--)
            for(int k=10000;k>=cnt5[i];k--)
                dp[j][k]=max(dp[j][k],dp[j-1][k-cnt5[i]]+cnt2[i]);
    for(int i=0;i<=10000;i++)
        ans=max(ans,min(i,dp[m][i]));
    printf("%lld",ans);
    return 0;   
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值