模拟题——数17 的简单解法

数17
Problem Description
小cos是寒树中学一名机敏睿智的男生。最近他偶然地发现同班的小cot——极为神秘的女生——正想方设法地收集他的信息。小cos非常不解。这天他收到了来自小cot的一封邮件,要他数出从a到b(包括a和b)的正整数中共有多少个“17”。(如817517有两个“17”)
小cot打着什么算盘?“17”蕴含着什么不为人知的秘密?……不,你不需要管这些……你的任务是回答小cot的问题。
小cos是寒树中学一名机敏睿智的男生。最近他偶然地发现同班的小cot——极为神秘的女生——正想方设法地收集他的信息。小cos非常不解。这天他收到了来自小cot的一封邮件,要他数出从a到b(包括a和b)的正整数中共有多少个"17"。(如817517有两个"17")
小cot打着什么算盘?"17"蕴含着什么不为人知的秘密?……不,你不需要管这些……你的任务是回答小cot的问题。
Input
第1行两个正整数a和b,意义见问题描述。
Output
输出一个整数,表示a到b的正整数中"17"的数量。
Sample Input
1 200
Sample Output
12
Data Constraint
对于30%的数据,有a≤b≤1,000
对于50%的数据,有a≤b≤100,000
对于100%的数据,有a≤b,a,b均在 int 范围内。

首先,看到题目,我们很容易知道,它询问的是,对于一个区间 [a,b],里面所有的数中,有多少个"17",例如 117 有1个"17", 1717171 有6个"17"。
那么,我们很容易做出一个简单的暴力程序,计算出一个数有多少个"17"。
代码如下。

#include <cstdio>

#define LL long long 

using namespace std;

LL a,b,ans;

LL find(LL a)
{
	LL p=0;
	while (a)
	{
		if (a%100==17)
			p++,a/=100;
		else
			a/=10;	
	}
	return p;
}

int main()
{
	scanf("%lld%lld",&a,&b);
	for (LL k=a;k<=b;k++)
		ans+=find(k);
	printf("%lld",ans);
	return 0;
}

这个程序可以帮助我们做几件事。

  • 直接作为代码提交可以拿到部分分。
  • 可以用计算机暴力经过一段时间,以此求出所有情况,然后用分块的方法暴力来大大降低复杂度。例如,如果我们将17的数量以105分块,那么,我们的复杂度最大就是 2*105+104,也就是所有分块的求和,以及左右两端这两块的单独求和。那么,这个复杂度就相当的低了。因为int最大值为2147483647(231-1)
  • 其次,这可以作为我们正确算法的一个验算的根据。在我们算法的正确性不能得到保障的情况下,经过多次大数据的验算是一个非常有效的验证算法正确性的方法。

所以,我们就开始思考正确方法。
我们知道,如果我们要判断一个数x有没有17,那么判断的次数应该是β(β=x的位数)。而对于a~b的数都要进行判断,很显然,这种方法是极其不优的。
那么,我们可以逆向思维。我们不选择计算一个一个数含有"17",而是反过来,我们枚举每两个相邻的位上的"17",并计算出在这两个位是"17"的情况下,有多少个数满足条件。而这样,我们就可以运用组合数学的方法进行进一步处理。
当然,补充一下,我们确定两位是"17"后,计算出[a,b]总共有多少个数满足这两位是"17",也可以扩展一下,就是[1,b]有多少个数满足这两位是"17",并用它减去[1,a-1]有多少个数满足这两位是"17"。
因此,我们不断枚举两个位是"17",并且计算出此时满足在[1,b]、[1,a-1]上的数的值并且不断做差。
那么,我们就可以通过这样的值进行求和。
不过,我们需要明确的是,对于一个数1717在[a,b]范围内,我们枚举百位、前位是"17"时,就会考虑到这个数1717,而在我们枚举个位、十位是"17"时,也会考虑到这个数1717,那么,它就被考虑了两次,正好与其1717有两个"17"相应,所以,我们可以很确定的知道,我们只需要枚举"17"的位置,而无需在意考虑的数是否还有别的"17",因为其如果还有别的"17",必然会在另外的枚举中被考虑到。
所以,我们可以写出如下代码。

#include<cstdio>

#define LL long long 

using namespace std;


LL c[14];
LL ans,a;

LL find(LL );

int main()
{
	c[0]=c[1]=1;
	for (LL k=2;k<=13;k++)
		c[k]=c[k-1]*10;
	scanf("%lld",&a);
	a--;
	ans=find(a);
	scanf("%lld",&a);
	printf("%lld",find(a)-ans);
	return 0;
}

LL find(LL x)
{
	if (x<17)
		return 0;
	LL m=13,out=0;
	while (c[m]>x)
		m--;
	m++;
	LL y,now;
	for (LL k=m;k>=3;k--)
	{
		y=x%c[k];
		now=y/c[k-2];
		out+=(x/c[k])*c[k-2];
		if (now>17)
			out+=c[k-2];
		else
			if (now==17)
				out+=(k==3?1:((y%c[k-2])+1));
	}
	return out;
}

猜你喜欢

转载自blog.csdn.net/CutieDeng/article/details/82857550
今日推荐