例题10-22 统计问题 UVa1640

1.题目描述:点击打开链接

2.解题思路:本题要求数从a到b这串数字中0~9这10个数的个数。这类问题的一般步骤都是先用f(n,d)计算出0~n中d数字出现的次数,那么答案就是f(b,d)-f(a-1,d)。计算f(n)函数时,可以考虑递推法计算,下面举例解释计算过程:

比如1~2974这个序列,将它拆成1~2970和2971~2974两部分,后者就是个位从1到4,这4个数字先各出现一次,然后2,9,7这3个数字分别出现了5次(虽然是从2971开始,但2970也要算上);前者的数位统计利用递推来解决,先只统计2969个位的这10个数出现的全部次数(从0数起),每个数都出现了297次。然后只用重复上述步骤,计算296这个数即可,但这时就相当于10个数地计算了。即每次向下一层计算时,乘子m要乘以10。归纳起来其实就是找f(k)和f(10*k+m)之间的关系。

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define N 10+5
int a[N], b[N];

void copy(int*a, int*b)
{
	for (int i = 0; i < 10; i++)
	{
		a[i] = b[i];
		b[i] = 0;
	}
}

void calculate(int n, int m)//m是乘子,每次进入下一层自乘10
{
	int x = n / 10;//不含末位的部分
	int y = n % 10;//末位
	int temp = x;
	for (int i = 1; i <= y; i++)//先数一数从10*x+1到10*x+y这段序列中不超过个位数的数的个数
		b[i] += m;
	for (int i = 0; i < 10; i++)//计算10*x-10到10*x-1这段序列中个位数的总个数
		b[i] += m*x;
	while (temp)
	{
		b[temp % 10] += m*(y + 1);//再数一数从10*x+0到10*x+y这段序列中非个位数的数的个数,注意不要算成10*x+1到10*x+y了
		temp /= 10;
	}
	if (x)
		calculate(x - 1, m * 10);//递归解决
}
int main()
{
	//freopen("test.txt", "r", stdin);
	int aa,bb;
	while (cin >> aa >> bb,aa||bb)
	{
		if (aa > bb)swap(aa, bb);
		memset(b, 0, sizeof(b));
		calculate(aa - 1, 1);
		copy(a, b);
		calculate(bb, 1);
		for (int i = 0; i < 10; i++)
			printf("%d%c", b[i] - a[i], i == 9 ? '\n' : ' ');
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值