【分治算法】统计数字问题

在这里插入图片描述

暴力法

#include<bits/stdc++.h>
using namespace std;

int main() {
	int n;
	cin>>n;
	int a[10]= {0};
	for(int i=1; i<=n; i++) {
		int t=i;
		while(t) {
			a[t%10]++;
			t=t/10;
		}
	}
	for(int i=0; i<10; i++) {
		cout<<i<<" : "<<a[i]<<endl;
	}
	return 0;
}

递归法

在这里插入图片描述

参考文章:

1-1统计数字问题(详解)
步骤一:
先求键盘输入的数n是一个几位数,设位数为len

公式log(n)+1;

可用cmath里的函数:double log10(double x)来求解
步骤二:
划分区间,然后相加

把数n尽可能的划分为较少的区间。【按位数划分】

223,可划分成00-99,100到199(两个区间)
3333,可划分为000-999,1000到1999,2000到2999(三个区间)
后两个区间比第一个区间分别多了1000个1和2,其余数字相同。
在这里插入图片描述
在这每一个区间内(除了最高位)0-9出现的次数是**(len-1)*10^(len-2)**----(列举一下可发现规律)

现在我们知道了每个区间的0-9出现的次数,该求区间数了:

如233区间数就是2,3333的区间数就是3,所以数n的区间数p是n/(10^(len-1))

综上,如3333,从000-2999(除了最高位)0到9的每个数的个数为p(len-1)10^(len-2)

以3333为例,上面的那个公式都没有计算千位数出现的次数,而是把划分好的区间计算完了,3333分为三个区间,区间数3乘以0-9出现的次数,其实就是计算了三次000-999出现的次数。

步骤三:
最高位相加

如3333,上一步我们已经算好从000到2999,(除了最高位)0到9每个数的和了

现在来算最高位

最高位(第4位)的数有0、1、2、3

0:0000-0999共1000个

1:1000-1999共1000个

2:2000-2999共1000个

3:3000-3333共333+1个

设最高位为m可得代码如下

for(int i=0;i<m;i++){
    c[i]+=pow(10.0,len);
}
c[m]+=1+n%((int)pow(10.0,len-1));

步骤四:
递归

处理完3333的000-2999个数以及3作为第len位的情况剩下333递归处理333

即要处理t=n%(10^(len-1))

注意:

(1)若t为0,比如200,此时c[0]要加len-1也就是2!

若数10,则t为0,c[0]要加len-1,也就是1,

然后结束递归.

(2)若t不为0,这里特判一下,若lenT!=len-1说明是诸如10010这种情况,中间两个0还是要处理的,

c[0]+=(len-lenT-1)*(t+1)

fill()函数参数:fill(first,last,val);
// first 为容器的首迭代器,last为容器的末迭代器,val为将要替换的值。

注意: fill()中 ,它的原理是把那一块单元赋成指定的值,也就是说任何值都可以
memset(),则是将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,即0 、1

#include<bits/stdc++.h>
using namespace std;
//统计数字问题  递归法
int c[10];

void solve(int n) {
	int len=log10(n)+1;//数n的位数
	int p=n/((int)round(pow(10.0,len-1)));//最高位的值
	for(int i=0; i<10; i++)
		c[i]+=p*(len-1)*(int)round(pow(10.0,len-2));
	for(int i=0; i<p; i++)
		c[i]+=(int)round(pow(10.0,len-1));
	int t=(int)round(pow(10.0,len-1));
	t=n%t;
	if(t==0) {
		c[p]++;//最高位+1
		c[0]+=len-1;//0位+len-1
		return ;
	}
	int lent=log10(t)+1;
	if(lent!=len-1) {
		c[0]+=(len-lent-1)*(t+1);
	}
	c[p]+=t+1;
	return solve(t);
}

int main() {
	int n;
	//下面做了一个循环输入的设计 当然也可以删除 
	while(cin>>n) {
		fill(c,c+10,0);
		int len=log10(n)+1;
		solve(n);

		for(int i=0; i<len; i++) {
			c[0]-=(int)round(pow(10.0,i));
		}

		for(int i=0; i<10; i++) {
			cout<<i<<" : "<<c[i]<<endl;
		}
	}

	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摆烂.MVP

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值