【蓝桥杯】【丑数】【算法优化】第几个幸运数

题目链接

https://www.luogu.com.cn/problem/U136049

我只是大自然的搬运工,这题目是蓝桥杯的一道真题。我出了几个测试数据,就把它放到了洛谷的个人题库。

题目描述

  到x星球旅行的游客都被发给一个整数,作为游客编号。x星的国王有个怪癖,他只喜欢数字3,5和7。 国王规定,游客的编号如果只含有因子:3,5,7,就可以获得一份奖品。

  我们来看前10个幸运数字是: 3 5 7 9 15 21 25 27 35 45。 因而第11个幸运数字是:49

  小明领到了一个幸运数字 x,他去领奖的时候,人家要求他准确地说出这是第几个幸运数字,否则领不到奖品。

输入格式

小明领取到的幸运数字 x。 1 <= x < 10^14。

输出格式

一个正整数,表示这是第几个幸运数字

输入输出样例

输入 #1复制

49

输出 #1复制

11

说明/提示

1 <= x < 10^14

解法1

维护一个计数变量,暴力枚举从3到x,每个数都判断是否是幸运数,如果是幸运数,计数变量+1。

难点在于如何判断一个数是否是幸运数。由于一个幸运数的因子只有3,5,7。因此只需要让它不断除以3直到不能整除3为止,然后不断除以5直到不能整除5为止,然后不断除以7直到不能整除7为止。最后的结果如果是1,则原数就是幸运数。

但是对于x最大可达10^14,显然对于这样的数据规模,这种算法会超时。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long ll;

int main()
{
	
	ll x;
	scanf("%lld", &x);
	
	int ans=0;
	for(ll i=3; i<=x; i++)
	{
		ll t=i;
		while(t%3==0)
			t /= 3;
		
		while(t%5==0)
			t /= 5;
		
		while(t%7==0)
			t /= 7;
		
		if(t==1)
		{
			ans++;
			if(i==x)
				break;
		}
	}
		
	printf("%d", ans);
	
	return 0;
}

解法2

来自于某猛女 Orz。。。x=3^{a}\times 5^{b}\times 7^{c},那么可以通过三重循环枚举阶数,统计 3^{a}\times 5^{b}\times 7^{c}\leqslant x 的种数。 但是需要注意两个问题

1、pow(3,a)*pow(5,b)*pow(7,c) 可能会超出long long返回,所以不要用 long long 去接收。要用 double类型接收,而 pow() 的返回值本身就是 double

2、为什么 if 里面的条件是 < 而不是 <= ?因为这么枚举多算了 x=1(事实上这是不合法的),所以不取等号(不算上等于x的情况)刚好就抵掉了

#include<stdio.h>
#include<math.h>
int main(void) {
	long int n,m;
	scanf("%ld",&n);
	int a,b,c,count=0;
	for(a=0 ; pow(3,a)<n ; a++ ) {
		for(b=0 ; pow(5,b)<n ; b++) {
			for(c=0 ; pow(7,c)<n ; c++) {
				if(pow(3,a)*pow(5,b)*pow(7,c)<n) {
					count++;
				}
			}
		}
	}
	printf("%d",count);
	return 0;
}

 解法3

假设已经知道前 i–1 个幸运数,如何推出第 i 个幸运数? 显然第 i 个数可以由前 i-1 个数推出来。第 i 个数有可能是前 i-1 个数中的某个数乘以3得到,也可能是前 i-1 个数中的某个数乘以5得到,也可能是前 i-1 个数中的某个数乘以7得到。不太好表述,直接看代码,也比较好懂。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long ll;

vector<ll> num;

// 求 num[ num.size() ] 的待定值 
// 返回一个大于 num[ num.size()-1 ] 的待定值 
// 可用二分法优化,但没必要 
ll getNum(int key) 
{
	for(int i=0; i<num.size(); i++)
	{
		ll t = num[i]*key;
		if(t > num[ num.size()-1 ])
			return t;
	}
}

int main()
{
	num.push_back(3);
	num.push_back(5);	
	num.push_back(7);
	
	ll x;
	scanf("%lld", &x);
	
	while(num[ num.size()-1 ] < x) 
	{
		ll a = getNum(3);
		ll b = getNum(5);
		ll c = getNum(7);
		ll t = min(a, min(b, c));
		num.push_back(t);
	} 
	
	
	printf("%d", num.size());
	
	return 0;
}

解法4

显然,对于解法3的 getNum() 可以换成 二分法 来优化! 对比一下耗时,变化不大,因为 对于测试数据中最大的 x,  不定长数组 num 的元素个数也只是差不多2000而已,所以对于这个优化,效果不是很明显。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long ll;

vector<ll> num;

// 二分法 
ll getNum(int key) 
{
	int l=0, r=num.size()-1;
	
	while(l<r)
	{
		int m=(l+r)/2;
		if (num[m]*key <= num[ num.size()-1 ]) 
			l=m+1;
		else 
			r=m;
	}
	return num[r]*key;
}

int main()
{
	num.push_back(3);
	num.push_back(5);	
	num.push_back(7);
	
	ll x;
	scanf("%lld", &x);
	
	while(num[ num.size()-1 ] < x) 
	{
		ll a = getNum(3);
		ll b = getNum(5);
		ll c = getNum(7);
		ll t = min(a, min(b, c));
		num.push_back(t);
	} 
	
	
	printf("%d", num.size());
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43290318/article/details/109226775
今日推荐