数位DP解析

5 篇文章 0 订阅
3 篇文章 0 订阅

数位DP

如字面意思,是关于数字位数的一种DP。我们给定dp[i][j]定义为数字长度为i,最高位为j的所有数字集合所能产生的特定结果。例如i = 2,j = 1,最高位为十位数且最高位为1的所有数字中,1出现的次数dp[2][0] = 1,dp[2][1] = 11次,dp[2][2] = 1;
那么dp的转移方程式为:

for (int i = 1; i <= length; i++) {
    for (int j = 0; j <= 9; j++) {
        if (j == x) {
            dp[i][j] = (int) Math.pow(10, i - 1);
        }
        for (int k = 0; k <= 9; k++) {
            dp[i][j] += dp[i - 1][k];
        }
    }
}

对于一个小于等于n范围的所有数字的集合,它可以用三个部分来构成:

  • 所有位数小于数字n的位数的数字
  • 所有位数等于数字n位数,且最高位小于n最高位的数字
  • 所有位数等于数字n位数,最高位等于n最高位的数字

对于情况一而言,我们可以将所有长度小于当前n的长度len的数字集合用一个循环表示:

for (int i = length - 1; i >= 1; i--) {
    for (int j = 1; j <= 9; j++) {
        ans += dp[i][j];
    }
}

情况二同理:

for (int j = 1; j < bit; j++) {
    ans += dp[length][j];
}

bit代表数字n的当前最高位值。
情况三等同于去除最高位后,长度等于len - 1的数字又由情况二和情况三来构成。所以我们可以使用递归或者迭代的思想完成对于数字n的数位dp。
代码如下:

public int countDigitX(int n, int x) {
     // dp[i][j] 表示第 i 位作为最高位时值为 j 时 x数字 的出现次数
     int[][] dp = new int[11][11];
     int length = 0, t = n;
     while (t > 0) {
         length++;
         t /= 10;
     }
     length = Math.max(1, length);

     for (int i = 1; i <= length; i++) {
         for (int j = 0; j <= 9; j++) {
             if (j == x) {
                 dp[i][j] = (int) Math.pow(10, i - 1);
             }
             for (int k = 0; k <= 9; k++) {
                 dp[i][j] += dp[i - 1][k];
             }
         }
     }
     if(n <= 9) {
         int ans = 0;
         for (int i = 0; i <= n; i++) {
             ans += dp[1][i];
         }
         return ans;
     }
     int ans = 0;
     int bit = n / (int)Math.pow(10, length - 1) % 10;
     for (int j = 1; j < bit; j++) {
         ans += dp[length][j];
     }
     for (int i = length - 1; i >= 1; i--) {
         for (int j = 1; j <= 9; j++) {
             ans += dp[i][j];
         }
         if (bit == x) {
             var sum = n % (int)Math.pow(10, i) + 1;
             ans += sum;
         }
         bit = n / (int)Math.pow(10, i - 1) % 10;
         if (i == 1) {
             bit++;
         }
         for (int j = 0; j < bit; j++) {
             ans += dp[i][j];
         }
     }
     return ans;
 }

值得注意的是,从整个代码可以看出来,只有现在迭代的长度等于1的时候,我们需要获取j小于等于当前bit的值,其余时候j都应该是小于当前bit的值。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值