• CF1428G Lucky Numbers


    一、题目

    点此看题

    二、解法

    首先观察权值完全是由数位决定的,我们考虑按位规划,每一位可以选不超过 (3k)(3),每一个 (3) 贡献是 (F_i)

    这样算出的结果很可能不合法,有些数位是会为了满足题目限制而选取 (0,3,6,9) 以外的数。

    结论:最优解中每个数位最多有一个选取 (0,3,6,9) 以外的数。因为如果出现了两个及以上可以通过调整只剩一个,而且调整之后的解一定更优。

    那么把所有特别的数位都集中在一个数里,设 (dp[i]) 为总和为 (i) 的最优解,那么初始化 (dp[x]) 为数字 (x) 的权值,也就是直接把这个数考虑了。那么剩下的问题可以按位规划了,每一位可以选不超过 (3k-3)(3),每一个 (3) 贡献是 (F_i)

    不难发现这是一个多重背包问题,可以二进制优化多重背包,时间复杂度 (O(nlog n))

    三、总结

    还是解决限制类问题,可以通过特殊考虑使问题变简单。

    背包问题的特殊结论:有一个物品是特殊的,它的选取方式和其他物品不一样,特殊考虑它就能让问题变简单。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 1000005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,k,q,a[M],dp[M];
    void get(int v,int c)
    {
        for(int i=n;i>=v;i--)
            dp[i]=max(dp[i],dp[i-v]+c);
    }
    void work(int v,int c)
    {
        int now=min(k,n/v);
        for(int i=1;i<now;i<<=1) get(v*i,c*i),now-=i;
        get(v*now,c*now);
    }
    signed main()
    {
        n=1e6;k=3*read()-3;
        for(int i=0;i<=5;i++)
            a[i]=read();
        for(int i=1;i<=n;i++)
        {
            int now=0,x=i;
            while(x)
            {
                int t=x%10;
                if(t%3==0) dp[i]+=(t/3)*a[now];
                now++;x/=10;
            }
        }
        for(int i=0,sz=1;i<=5;i++)
            work(sz*3,a[i]),sz*=10;
        q=read();
        while(q--) printf("%lld
    ",dp[read()]);
    }
    
  • 相关阅读:
    SQL整理5
    SQL整理1 数据类型
    SQL整理2
    JavaScript 的DOM操作
    JavaScript 数据类型
    JavaScript
    CSS样式表
    sqlserver数据库 提纲
    Python基础第十二天:二分法算法
    Python基础第十一天:递归函数
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15089204.html
Copyright © 2020-2023  润新知