51nod 1042 数字0-9的数量(数位dp)

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

思路:

方法一:求当前数位上出现目标数p的次数 sum 。设当前数位上的数为 x;有三种情况:

1.x>p   则sum 受到当前位以及高位的影响。 

2.x==p 则sum 受到低位以及高位的影响

3.x

51nod 1009 数字1的数量 就是用的这种方法,可以看博客https://blog.csdn.net/BBHHTT/article/details/80243363,更详细。

方法二:求0到由低位到当前数位所组成的数 出现 p的次数。同样有三种情况:

1.x>p   则sum 受到当前位及低位的影响

2.x==p 则sum 受到低位的影响

3.x

方法二更绕,没方法一好理解。注释比别的博客多多的博客:https://blog.csdn.net/f_zyj/article/details/52082449,也是根据这个注释看懂了,但还是第一个好理解。

#include
#include
#include
#include
using namespace std;
#define ll long long
ll dp[20];//前x位(从低位开始的)出现目标k的次数(0-9都一样) 
void init()
{
	for(int i=1;i<=18;i++) {
		dp[i]=dp[i-1]*10+pow(10,i-1);//前x-1位出现k次数*10 + 第x位为k的情况数
	}//例如 两位数XX:出现1的个数为dp[2]=20. (0-9都一样) 
}
ll fun(ll num,ll k) {//k是目标 
	ll pos,x,sum,temp,tail,len;
	x=num;
	sum=0;
	temp=1;//代表10的多少多少次方
	len=0;//位数长度 
	tail=0;//当前位为k的个数。 
	while(num) {
		pos=num%10;
		num/=10;
		len++;
		if(pos>k) {                  //  temp就代表10的多少多少次方	
			sum+=temp+pos*dp[len-1];//  ex:241 temp(200~241) += temp(当前位为k的数量,即百位为k)+ digit
		}                           //  * dp[len - 1](000~199除去百位为k的情况,即000-099,200-241)
		else if(pos==k) {
									   //  +1是代表取的那个整数
			sum+=tail+1+pos*dp[len-1];// ex:141 result(100~141除去百位为1的情况) += tail(101~141百位
		}                             //  为1) + 1(100的百位为1) + 1*dp[len - 1](0~99)
		else {                       
			sum+=pos*dp[len-1];		
		}
		tail+=pos*temp;//当前位为k的情况的个数。
		temp*=10;
	}
	if(k==0) {//减去前缀为0的情况。 
		ll m=1;
		while(x) {
			sum-=m;
			m*=10;
			x/=10;
		}
	}
	return sum;
}
int main()
{
	ll a,b;
	init();
	scanf("%lld%lld",&a,&b);
	for(int i=0;i<=9;i++) {
		printf("%lld\n",fun(b,i)-fun(a-1,i));
	}
}

 

你可能感兴趣的:(动态规划,2级算法题)