文章标题

来源:互联网 发布:出租屋网络千兆方案 编辑:程序博客网 时间:2024/06/07 14:53

这道题目与leetcode 600 有一些相似, 所以博主一开始用的是相类似的解法. dp[n][i]记录的是位数<=n的数中, 包含i个1的数的个数.
1. dp[n][0] = dp[n-1][0] + 8*dp[n-1][0] = dp[n-1][0] * 9.
其中, 第一部分表示位数<=n-1的数中, 包含0个1的个数. 第二部分表示位数等于n的数, 包含0个1的个数. 第n位可以选择2, 3, …9这八个数.
2. dp[n][i] = dp[n-1][i] + dp[n-1][i-1] + 8*dp[n-1][i]
其中第一部分表示位数小于等于n-1的包含i个1的数的数量. 第二部分表示位数为n, 且第n位为1. 则剩下的部分只要满足有[i-1]个1就可以了. 第三部分表示位数也是n位. 但是第n位选择2, 3, … 9


假设n的位数为N,在得到这个dp之后我们可以把问题拆解为两部分: 位数为N的数中包含的1数量. 位数小于N的数中包含的1的数量. 第二部分很好求:

for(int c = len-1; c>=1; --c) {    cnt += dp[len-1][c]*c;}

zhzz至于第一部分, 因为必须<=n, 所以我们需要逐一位处理:
先处理小于n的数,
考虑第N位:

53144xxx3xxx2xxx1xxx第N位可以取4, 3, 2, 1

剩下的位:

531452xx51xx50xx第N-1可以取2, 1, 0

int countDigitOne(int n) {    if(0 == n) {        return 0;    }    int len = 1;     long mask = 10;    while(n >= mask) {        ++len;        mask *= 10;    }    vector<vector<int>> dp(len+1, vector<int>(len+1, 0));    dp[0][0] = 1;    dp[1][0] = 9;    dp[1][1] = 1;    for(int l = 2; l <= len; ++l) {        dp[l][0] = dp[l-1][0] * 9;        for(int c = 1; c <= l; ++c) {            dp[l][c] = dp[l-1][c] * 9 + dp[l-1][c-1];        }    }    mask /= 10;    int cnt = 0;    /*size = len*/    int prev_1_cnt = 0;    for(int i = len; i >= 1; --i) {        const int digit = n/mask;        int not1_head = 0;        if(i == len) {            not1_head = digit - 2;        }else if(digit == 1){            not1_head = 1;        }else {            not1_head = digit - 1;        }        if(not1_head >= 1) {            for(int l = 0; l <= i-1; ++l) {                cnt += not1_head * dp[i-1][l] * (prev_1_cnt+l);            }        }        if(digit > 1) {            for(int c = 0; c <= i-1; ++c) {                cnt += dp[i-1][c] * (prev_1_cnt + c +1);            }        }        n = n % mask;        mask /= 10;        if(digit == 1) {            ++prev_1_cnt;        }    }    /*size < len*/    for(int c = len-1; c>=1; --c) {        cnt += dp[len-1][c]*c;    }    cnt += prev_1_cnt;    return cnt;}

a前面的方法还是非常繁琐的. 这里再引入StefanPochmann 大神的解法

对于一个数. 我们计算能有多少个数个位上有1, 十位上有1, 百位上有1…把这些数都加起来就是我们要的结果:
a我们只分析百位上的情况:
n=3141592

31415920-3141|1|0-99总共有(3141+1)*100个数

n=3141192

31411920-3140|1|0-993141|1|0-91总共有3141*100 + 92

能n=3141092

31410920-3140|1|0-99共有3141*100

int countDigitOne(int n) {    if(0 == n) {        return 0;    }    long mask = 1;    int cnt = 0;    while(n >= mask) {        const int a = n / mask;        const int b = n % mask;        const int v = a%10;        if(v > 1) {            cnt += (a/10 + 1) * mask;        }else if(v == 1) {            cnt += (a/10) * mask + b+1;        }else {            cnt += (a/10) * mask;        }        mask *= 10;    }    return cnt;}
原创粉丝点击