洛谷P1980 [NOIP2013 普及组] 计数问题(用一层循环解决双重循环的问题)

P1980 [NOIP2013 普及组] 计数问题

https://www.luogu.com.cn/problem/P1980?contestId=85480
内存限制:128 MiB
时间限制:1000 ms
标准输入输出
题目类型:传统
评测方式:文本比较
    
题目描述
试计算在区间 1 到 n的所有整数中,数字 x(0≤x≤9)共出现了多少次?例如,在1到11中,即在 1、2、3、4、5、6、7、8、9、10、11 中,数字 1 出现了 4次。

输入格式
输入共 1 行,包含 2 个整数 n、x,之间用一个空格隔开。

输出格式
输出共 1行,包含一个整数,表示 x 出现的次数。

样例
样例输入1:
11 1

样例输出1:
4

数据范围与提示
数据范围:
对于 100%的数据,1≤n≤1,000,000,0≤x≤91≤n≤1,000,000,0≤x≤9。

解题思路

本题可以用双重循环做,也可以只用一层while循环。用双重循环做的话会比较很多次,特别是位数比较高的数。而用while做,思维难度比较大,其中运用了排列组合的思想,但时间复杂度很低,九位数只需要9次比较。下面这个代码是用第二种方法做的:

#include
#include
using namespace std;
int h,a,c,b,n,cnt,m,k;
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>k;
	int t=n;
	while(t>0){
		h=pow(10,m); 
		a=n/h%10;
		c=n/(h*10),b=n%h;
		if(k==0&&a==0)
			cnt+=h*(c-1)+b+1;
		else if(a==k)
			cnt+=c*h+b+1;
		else if(a<k)
			cnt+=c*h;
		else
			cnt+=(c+1)*h;
		m++; 
		t/=10;
	}
	cout<<cnt; 
}

这道题需要考虑0的情况;

	int t=n;//这一步为了循环时不改变n的大小,所以又定义了一个t用来代替n;
	h=pow(10,m); //m默认初始值为0,
	m++; //每当一轮循环结束时就m++,起到让h*10的作用;

以上两步让每一轮循环结束都m*10,方便给a,b进行降位。

		a=n/h%10;//a为当前位
		b=n%h,c=n/(h*10)//b,c分别为后一位和之前的位数

这一步求出了当前的位数,后一位的数,之前的位数;
当h越大时n/h的值就越小,h增大一个零,b,c就各减少一位。

		if(k==0&&a==0)
			cnt+=h*(c-1)+b+1;

这里表示如果k=0且a=0,(即满足条件),则cnt(计数用)加上现在的数*之前的位数,即当这个数位为0时,所出现过的次数,再加上后一位数+1即可;

	else if(a==k)
			cnt+=c*h+b+1;

本段和上一段的意义不同在于上一个判断的是0的情况这一个不是,所以多加了1个h*c,其实上一个段在思想上也差不多,只不过,上一个当前位为0,可以省略,而这一个不行。

	else if(a<k)
			cnt+=c*h;

本段意为:若a

	else
		cnt+=(c+1)*h;

当a>k时,这一个数位另外多出了前面的数*当前位数个数,所以c需加1;

		t/=10;

用t来判断当前数是否>0。

这道题的精髓就在于运用了排列组合的思想,以每一位为单位进行计算,大大减少了时间复杂度;

写在最后:今天因为时间仓促,写得不够精细,之后还会优化此篇文章。

你可能感兴趣的:(题目解析,c++)