【51nod】--1009数字1的数量&&1042数字0-9的数量(数位dp)

看了一篇写的超级超级好的博客真的!厉害
1009 数字1的数量
基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 收藏 关注
给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数。
例如:n = 12,包含了5个1。1,10,12共包含3个1,11包含2个1,总共5个1。
Input
输入N(1 <= N <= 10^9)
Output
输出包含1的个数
Input示例
12
Output示例
5

1042 数字0-9的数量
基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注
给出一段区间a-b,统计这个区间内0-9出现的次数。
比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次。
Input
两个数a,b(1 <= a <= b <= 10^18)
Output
输出共10行,分别是0-9出现的次数
Input示例
10 19
Output示例
1
11
1
1
1
1
1
1
1
1

思路分析:
数位dp;
先看第一题哦,在上面大神的博客可以理解到,每一位上1的个数都和本位上的数字,以及高位和低位的数字都有关。
举几个栗子吧:
302:{ 个位‘2’上1的个数:1,11,21,,91,101,,191,201,,291,301
1+9+10+10+1=31;
十位‘0’上1的个数:10,,19,110,,119,210,,219
10+10+10=30;
百位‘3’上1的个数:100,101,,199
100;
}
312:{个位‘2’上1的个数:1,11,,91,101,,191,201,,291,301,311
1+9+10+10+2=32;
十位‘1’上1的个数:10,,19,110,,119,210,,219,310,,312
10+10+10+3=33;
百位‘3’上1的个数:100,101,,199
100;
}
322:{个位‘2’上1的个数:1,11,,91,101,,191,201,,291,301,,321
1+9+10+10+3=33;
十位‘2’上1的个数:10,,19,110,,119,210,,219,310,,319
10+10+10+10=40;
百位‘4’上1的个数:100,101,,199
100;
}

由上面可看出:
302:{个位:(30+1)*10^0=31
十位:3*10^1=30
百位:(0+1)*10^2=100
}
312:{个位:(31+1)*10^0=32
十位:3*10^1+1+2=33
百位:(0+1)*10^2=100
}
322:{个位:(32+1)*10^0=33
十位:(3+1)*10^1=40
百位:(0+1)*10^2=100
}
分析可以得出
{
本位上的数字小于1时,该位上1的个数为:所有高位数字乘以本位对应10的幂;(个位为1,十位为10,百位为100、、、、)
本位上的数字等于1时,该位上1的个数为:所有高位数字乘以本位对应10的幂+1+低位所有的值;
本位上的数字等于1时,该位上1的个数为:(所有高位数字+1)*本位对应10的幂;
}
代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int ant=1,pre=1,lat=0,sum=0,num;
        while(n)
        {
            num=n%10;//从个位往前,每次循环得到的一个num就是该轮的一个本位 
            n/=10;//每一轮的n就是本位的高位 
            if(num==0)
                sum+=n*ant;//sum代表1的个数 
            else if(num==1)
                sum+=n*ant+1+lat;
            else if(num>1)
                sum+=(n+1)*ant;
            lat+=num*ant;//lat表示本位后面所有的低位 
            ant*=10; //ant代表本位对应的10的幂,即个位为1,十位为10,百位为100 
        }
        printf("%d\n",sum);
    }
    return 0;
} 

第二个题目和第一个有异曲同工之妙哦!
分析可以得出2~9的思路和1是一样的,只有0比较独特呢,最高位不能有0是吧。
还举栗子哦
302:{个位:10,,90,100,,190,200,,290,300
9+10+10+1=30;
十位:100,,109,200,,209,300,,302
10+10+3=23;
}
322:{个位:10,,90,100,,190,200,,290,300,,302
9+10+10+3=32;
十位:100,,109,200,,209,300,,309
10+10+10=30;
}
可以看出:
302:{个位:30*10^0=30;
十位:(3-1)*10^1+1+2=23;
}
322:{个位:32*10^0=32;
十位:3*10^1=30;
}
分析得到:
{
本位为0,则本位上0的个数为:(高位数字-1)*本位对应10的幂+1+低位;
本位不为0,则本位上0的个数为:高位数字*本位对应10的幂;
}
计算某一区间【st,ed】内某数字的个数等于【1,ed】-【1,st-1】的个数哦;
代码如下:

#include<cstdio>
#include<algorithm>
typedef long long LL;
using namespace std;
int main()
{
    LL st,ed,n,m;
    while(~scanf("%lld%lld",&n,&m))
    {
        n--;
        for(LL i=0;i<=9;i++)
        {
            st=n,ed=m;//下面的循环中会改变st,ed值,所以每次循环都要重新赋值 
            LL rem;
            LL power=1,sum1=0,sum2=0;
            LL last=0;
            if(i!=0)
            {
                while(st)
                {
                    rem=st%10;
                    st/=10;
                    if(rem<i)
                        sum1+=st*power;
                    else if(rem==i)
                        sum1+=st*power+last+1;
                    else
                        sum1+=(st+1)*power;
                    last+=rem*power;        //马虎哦这里 
                    power*=10;
                }
                power=1,last=0;
                while(ed)
                {
                    rem=ed%10;
                    ed/=10;
                    if(rem<i)
                        sum2+=ed*power;
                    else if(rem==i)
                        sum2+=ed*power+last+1;
                    else
                        sum2+=(ed+1)*power;
                    last+=rem*power;
                    power*=10;
                }
            }
            else
            {
                while(st/10>=1)//最高位不能为0,所以必须至少有两位数字 
                {
                    rem=st%10;
                    st/=10;
                    if(rem==i)
                        sum1+=(st-1)*power+1+last;
                    else
                        sum1+=st*power;
                    last+=rem*power;
                    power*=10;
                }
                power=1,last=0;
                while(ed/10>=1)
                {
                    rem=ed%10;
                    ed/=10;
                    if(rem==i)
                        sum2+=(ed-1)*power+1+last;
                    else
                        sum2+=ed*power;
                    last+=rem*power;
                    power*=10;
                }
            }
            printf("%lld\n",sum2-sum1);
        }
    }
    return 0;
} 
/*
1212 2240

303
1098
583
313
304
303
303
303
303
303
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值