蓝桥学院ACM入门基础--第一节 枚举+优化套路(1)#平方十位数--蓝桥杯2017国赛题#STL-set用法#数据类型long long

- 第一节 枚举+优化套路(1)-

枚举的基本思想:

枚举算法的基本思想非常朴素:就是利用计算机强大的运算能力,一个一个试,把答案试出来。
举个例子:我们要求1到100万里有多少个质数。这个答案我没办法用公式或者什么办法直接算出来,只好一个一个试1是不是质数、2是不是质数、3是不是质数……一直试到100万。这就是枚举

枚举的要点:

如果我一个算法根据数据范围、时限我知道要超时,我们怎么减少复杂度?
方法不外乎就是
1减少枚举的变量,原来我要枚举4个变量,能不能只枚举3个? 
2就是缩小枚举范围,我能不能通过改变枚举的变量,减少范围?

      确定需要的枚举变量

      确定枚举的范围(不重不漏)

1是二分,二分查找、二分搜索非常有效,一般是复杂度从O(N)降到O(logN)。使用范围也很广,我们会在后面专门拿出一节时间来讲。
2是用Hash,空间换时间。
3此外还有一些常用的套路:比如双指针,Leetcode上对应的分类是two pointer,直译过来就是双指针,大概的思想就是滑动窗口。
4还比如前缀后缀和,也是空间换时间的思路,后面我们会讲到。

                           二分

                           哈希

                          双指针

                          前缀、后缀和

      优化

我们先来看一道题,叫做平方十位数。是蓝桥杯2017年决赛题。

 标题:平方十位数
由0~9这10个数字不重复、不遗漏,可以组成很多10位数字。
这其中也有很多恰好是平方数(是某个数的平方)。
比如:1026753849,就是其中最小的一个平方数。
请你找出其中最大的一个平方数是多少?

注意:你需要提交的是一个10位数字,不要填写任何多余内容。

当然这是一道填空题,还不是编程题。不过我们可以从这个题来讨论一下枚举的优化。首先我们的一个思路可能就是这个样子:
 

思路1:

1、直接从大到小枚举可能的答案。最大的是9876543210,最小的是题目中给的1026753849。
2、然后我们去判断是不是恰好包含0~9十个数字。
3、再判断是不是完全平方数。
    1)令Y=int(sqrt(x))
    2)判断Y*Y==X?

这个算法当然正确。不过它的时间复杂度大概是10^10到10^11次方量级。因为首先枚举的范围就大约是10^10,然后分离各位数字也有一个O(10)的复杂度。

我们能不能优化这个算法?
当然是可以,我们不去枚举这个答案X是多少,改成枚举X的平方根Y是多少。因为X要求是完全平方数,所以Y也应该是整数嘛。

思路2:
1、枚举平方根,十位数的平方根,最小是30000,最大我们这里设的是100000,绰绰有余。
2、然后我们根据Y算出X,这样X一定是完全平方数,X=Y*Y
3、只用检查X是不是恰好0-9十个数字。

这个算法的复杂度就比之前的算法低了很多。枚举大概是10^5量级,判断是不是恰好十个数字还有O(10)的复杂度,总共大概是10^6量级。

主要来看一下判断是不是包含0~9的代码:

bool contains0_9(int x){
	if(x==0)return false;
	set<int> s;
	while(x){
		int d=x%10;
		s.insert(d);
		x/=10;
	}
	return s.size()==10;
}

这个while循环是在分离x的各位数字。每次把x的末位数字,也就是个位数字通过模10分离出来,存在变量d里。这个分离各位数字的方法是很常用的。
另外我们这里用了set来统计哪些数字出现过。Set是STL里非常好用的工具,由于它内部是平衡树实现的,所有插入、删除、查找等都是很优秀的复杂度O(logN)

STL-set用法

set跟vector差不多,它跟vector的唯一区别就是,set里面的元素是有序的且唯一的,只要你往set里添加元素,它就会自动排序,而且,如果你添加的元素set里面本来就存在,那么这次添加操作就不执行。要想用set先加个头文件set。

十位数 用long long

#include <iostream>
#include <set>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

bool contains0_9(long long x){
	if(x==0)return false;//这里没什么用,算法的严谨性 
	set<long long> s;
	//分离x的各位数字,每次把x的末尾数字分离出去
	while(x){
		int d=x%10;//d是x末位数字 
		s.insert(d);//把d插入s中 
		x/=10;//更新x 
	}
	return s.size()==10;
}
int main(int argc, char *argv[]) {
	//c语言中基本整型int占据两个字节,取值范围-2^15--2^15-1(-32768--32767)肯定不够
	//用 long long 
	for(long long i=100000;i--;i>30000){//找最大,倒着来 
		long long x=i*i;
		if(contains0_9(x)){
		cout << x << endl; 
		return 0; 
	    }
	} 
	return 0;
}

结果为:9814072356。

猜你喜欢

转载自blog.csdn.net/qq_34243930/article/details/81454746
今日推荐