LC第225场周赛A拓展-递推版数位dp

题目大意:

f ( i ) f(i) f(i) i i i的数位和,问你一个区间 [ L , R ] [L,R] [L,R] f ( i ) f(i) f(i)出现最多的 f ( i ) f(i) f(i)是多少,若有多解,输出任何一个.

1 ≤ L ≤ R ≤ 1 e 18 1 \leq L \leq R \leq 1e18 1LR1e18

题目思路:

由于范围太大,肯定是数位dp了。但是发现套数位dp模板并不好用。

①一种暴力的套模板方法:

按照套路:
d p 状 态 为 ( 前 缀 长 度 , 前 缀 数 位 和 , 是 否 顶 到 L , 是 否 顶 到 R ) dp状态为(前缀长度,前缀数位和,是否顶到L,是否顶到R) dp(,,L,R)时符合递推出口条件的数的个数

我们发现 f ( i ) , i ∈ [ L , R ] f(i),i \in [L,R] f(i),i[L,R]的不同取值为 l o g 10 R ∗ 9 log_{10}R * 9 log10R9.所以我们可以暴力跑 l o g 10 R ∗ 9 log_{10}R * 9 log10R9遍,每一遍的递归出口为 [数位和 = =   i == \ i == i].

所以我们可以得到 f ( i ) f(i) f(i)的每种可能取值的结果的出现次数,然后取最大值即可.

这种时间复杂度为: O ( ( l o g 10 R ) 3 ∗ 9 ∗ 9 ∗ 9 ) O((log_{10}R)^3*9*9*9) O((log10R)3999)

其中: l o g 10 R ∗ 9 log_{10}R * 9 log10R9来自枚举 f ( i ) f(i) f(i)的可能取值
l o g 10 R ∗ 9 log_{10}R * 9 log10R9来自数位dp时的数位和
l o g 10 R log_{10}R log10R来自数位dp时的数位和
9 9 9来自数位dp时填数的复杂度.

所以十进制下长度为 18 18 18范围内的数基本是该种方法的极限。

②大佬的更优秀的递推数位dp解法

还是设:
d p 状 态 为 ( 前 缀 长 度 , 前 缀 数 位 和 , 是 否 顶 到 L , 是 否 顶 到 R ) dp状态为(前缀长度,前缀数位和,是否顶到L,是否顶到R) dp(,,L,R)时[前缀数位和]的出现次数.

但是我们考虑使用递推的方法,因为我们发现以往的记忆化搜索方法往往需要到 d p dp dp出一个完整的结果才能计算答案。但是在这我们可以以递推刷表的方式从前往后转移答案。

这里放一个标准板子:

    string x , y;
    int dp[8][59][2][2];
    int countBalls(int lowLimit, int highLimit) {
        x = to_string(lowLimit);
        y = to_string(highLimit);
        int n = y.size() , m = n * 9;
        x = string(n - x.size() , '0') + x;
        dp[0][0][1][1] = 1;
        for (int i = 0 ; i < n ; i++){
            for (int j = 0 ; j <= m ; j++){
                for (int k = 0 ; k <= 1 ; k++){
                    for (int l = 0 ; l <= 1 ; l++){
                        if (dp[i][j][k][l] == 0) continue;
                        int low = (k ? x[i] - '0' : 0);
                        int up = (l ? y[i] - '0' : 9);
                        int val = dp[i][j][k][l];
                        for (int d = low ; d <= up ; d++){
                            int nj = j + d;
                            int nk = (k && d == low);
                            int nl = (l && d == up);
                            dp[i + 1][nj][nk][nl] += val;
                        }
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 1 ; i < 50 ; i++){
            int res = 0;
            for (int j = 0 ; j <= 1 ; j++)
                for (int k = 0 ; k <= 1 ; k++)
                    res += dp[n][i][j][k];
            ans = max (ans , res);
        }
        return ans;
    }

心得:
如何初始化string. = string(长度,填充字符);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值