zznu 1255 数字统计(数位DP, 数学方法)

最近在学数位DP, 感觉还是满有收获的! 做了几个题之后想起来自己OJ上曾经做的一道题,以前是用数学方法写的,现在改用数位DP来写了一遍。

题目:

1255: 数字统计

时间限制: 1 Sec  内存限制: 128 MB
提交: 31  解决: 4
[提交][状态]

题目描述

一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排, 
每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数 
字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1, 
2,…,9。

输入

给出表示书的总页码的整数n(1≤n≤2^31-1)

输出

输出10行,在第k行输出页码中用到数字k-1 的次数,k=1,2,…,10。

样例输入

11

样例输出

1
4
1
1
1
1
1
1
1
1

链接:http://acm.zznu.edu.cn/problem.php?id=1255

数学方法:

其实就是假设每一位是0-9然后进行判断, 计算出结果

#include<stdio.h>
int  Slove(int num,int k)
{
    int L, R, M, P = 1, a = num;
    int sum = 0;
    while(a)
    {
        R = num%P;
        M = a%10;
        L = a/10;
        if(k)
        {
            if(k < M)
                sum += (L+1)*P;
            else if(k == M)
                sum += L*P + (R+1);
            else
                sum += L*P;
        }
        else
        {
            if(a<10)
                break;
            if(k == M)
                sum += (L-1)*P + (R+1);
            else
                sum += L*P;
        }
        P *= 10;
        a /= 10;

    }
    return sum;
}

int main()
{
    int  n, ans;
    int i;
    scanf("%d",&n);
    for(i=0; i<=9; i++)
    {
        ans = Slove(n,i);
        printf("%d\n",ans);
    }
    return 0;
}

数位DP;

这个就比较通用了, 就是套模版, 对数据的处理上 稍加注意, 然后把状态确定好。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL dp[20][20][11][2];//dp[位数][数字出现的次数][这个数字是多少][首位是否是0]
int bit[20];

LL dfs(int pos,int cou,int num,int flag,bool is0)
{
    if( pos == -1 )
    {
        return  cou;
    }

    if(!flag && dp[pos][cou][num][is0] != -1)
        return dp[pos][cou][num][is0];

    LL ans = 0;
    int end = flag? bit[pos]: 9;

    for(int i=0; i<=end; i++)
    {
        if(i == num && !(is0 && i == 0) )
            ans += dfs(pos-1, cou+1, num, flag && i == end,  is0 && i == 0 );
        else
            ans += dfs(pos-1, cou, num, flag && i == end, is0 && i == 0 );
    }

    if(!flag)
        dp[pos][cou][num][is0] = ans;

    return ans;
}

void solve(LL n)
{
    int len = 0, i, m = n;
    LL ans;
    while(n)
    {
        bit[len++] = n%10;
        n /= 10;
    }

    for(i=0; i<=9; i++)
    {
        ans = dfs(len-1, 0, i, 1, 1);
        printf("%lld\n", ans);
    }
//    printf("\n\n\n");
}

int main()
{
    LL a;
    memset(dp, -1 ,sizeof(dp));

    while(scanf("%lld", &a) != EOF)
    {
        solve(a);
    }
//    printf("\n\n\n");
    return 0;
}
时间: 2024-12-27 00:10:57

zznu 1255 数字统计(数位DP, 数学方法)的相关文章

CodeForces 55D Beautiful numbers 数位DP+数学

题意大概是,判断一个正整数区间内有多少个整数能被它自身的每一个非零的数字整除. 因为每一个位置上的整数集s = {0,1,2,3,4,5,6,7,8,9} lcm(s) = 2520 现在有一个整数t是由s中一个或者多个数字构成的,记为abcde,显然t = a*10^4+b*10^3+c*10^2+d*10^1+e 要使得t能被a,b,c,d,e整除,必然有t % lcm(a,b,c,d,e) = 0 因为a,b,c,d,e去重之后一定是s的一个子集,所以lcm(s)一定是lcm(a,b,c,

HDU 3555 Bomb ,HDU 2089 深刻学习数位dp (各种方法乱用)

数位dp是与数位有关的区间统计 参考: 点击打开链接 思想是将数字拆分,dp[i][j] 长度为i的数字有多少个满足j条件的,从000~999 统计时,计当前数字为x,则其后面数字的变化的倍数是0~x-1 后面接000~999这样刚好统计不到这个数字n本身. eg: 对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制.此时如果能直接处理这一情况,则问题距离解决又会迈出一大步. 例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为: [10000,19999],[

【BZOJ-1833】count数字计数 数位DP

1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 2494  Solved: 1101[Submit][Status][Discuss] Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. Output 输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次.

BZOJ 1833 ZJOI 2010 count 数字计数 数位DP

题目大意:问0~9这10个数字在[l,r]中出现过多少次. 思路:数位DP.以前只是听说过,并没有写过,写了才发现好闹心啊.. 预处理一个数组,f[i][j][k]表示长度为i,开头为j,数字k出现的次数. 对于一个数kXXXXXX,我们先处理1~999999,然后处理1000000~kXXXXXX 前面的东西很规则,可以直接调用f数组来解决. 对于后面不太规则的东西,按位处理.总之就是乱搞,我也不太懂说不明白... CODE: #include <cstdio> #include <c

CodeForces 55D Beautiful numbers(数位dp+数学)

题目链接:http://codeforces.com/problemset/problem/55/D 题意:一个美丽数就是可以被它的每一位的数字整除的数. 给定一个区间,求美丽数的个数. 显然这是一道数位dp,就是满足一个数能被所有位数的lcm整除即可. 一般都会设dp[len][mod][LCM],mod表示余数,LCM表示前len位的lcm. 但是如果直接裸mod会很复杂,于是再想lcm{0,1,2,3,4,5,6,7,8,9}=2520; 而且lcm{a,b,c,d....}{a,b,c,

bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义f[i][j][k]表示若前i个位置有k个j的此时的全局方案数,然后就可以记忆化搜索了(具体看代码吧) 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath>

BZOJ 1833 数字计数(数位DP)

经典数位DP模板题. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath>

BZOJ1833: [ZJOI2010]count 数字计数 (数位dp)

传送门 数位dp... ...大概都是这个套路吧... ... 写这个的时候直接水了一发... ...我也不知道自己写的是不是dp... ... 大概是主要内容和dp关系不大的dp... ... mark代码..细长的代码真是丑啊..换行太频繁了.... 1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm&g

题解——[ZJOI2010]数字计数 数位DP

最近在写DP,今天把最近写的都放上来好了,,, 题意:给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. 首先询问的是一个区间,显然是要分别求出1 ~ r ,1 ~ l的答案,然后相减得到最终答案 首先我们观察到,产生答案的区间是连续的,且可以被拆分, 也就是说0 ~ 987的贡献= 0 ~ 900 + 901 ~ 987的恭喜, 同理,把位拆开也是等价的,所以我们可以单独计算每个位的贡献 这样讲可能有点不太清晰,举个例子吧 3872 我们先把它按数拆开来