noip数据结构与算法 之 基础小算法 枚举算法及其延伸思维
在noip乃至程序开发过程中,枚举算法是一种最常见,最基本,最简单的算法。它很符合我们思考问题的传统方式,易于我们理解,同时也很容易用代码来实现。
枚举的问题一般都很简单,以接下来的问题为例:
问题描述:
给定一个上界n,输出从0~n之间所有自然数是3的倍数的数。
输入数据:
只有一行n。
输出数据:
0~n之间所有3的倍数,以空格隔开。
输入样例:
10
输出样例:
3 6 9
数据范围:
输入数据保证3<n<=100000。
这道题有一个非常简单的思路,我只需要从i=3,4,5,6,7……n这么遍历一遍,然后用i%3==0来判断是不是3的倍数。总体来说不过几行代码。这就是枚举算法的思路,具体代码如下:
#include <iostream>
using namespace std;
int n;
int main(){
cin>>n;
for(int i=3;i<=n;++i){
if(i%3==0){
cout<<i<<” ”;
}
}
return 0;
}
什么?!这么简单就结束了嘛!?这次讲的东西也太水了吧!不!重要的还在后面呢!假设说还是同样的问题,这时候我的n的取值范围不再是100000这么小了,而是1000000000这么大!这可怎么办呀,我从3列到1000000000要这么多次运算,肯定会超时的呀。
这时候我们换一个思维方式,同样是枚举的算法,这次我开始列数。我知道3是3的倍数,我也知道3+3一定也是3的倍数,它是6。那我就一直+3+3+3…..直到+3完了之后超过1000000000时停止输出,我不就得到了3到1000000000里所有的3的倍数了嘛?这样的话操作次数是原来的1/3,而且省去了%运算,怎么想都比之前的枚举算法更高效嘛。这个算法代码如下:
#include <iostream>
using namespace std;
int n;
int main(){
cin>>n;
for(i=3;i<=n;i+=3){
cout<<i<<” ”;
}
return 0;
}
当然接下来我们还能再改,我现在给定l,r,输出从l到r区间内所有3的倍数。其实想起来很简单,就在刚刚的+3算法基础上做一个判断,判断l是不是3的倍数,如果不是找到比l大的最小的一个3的倍数。从它开始+3+3+3+3……边+3边输出。代码实现如下:
#include <iostream>
using namespace std;
int l,r;
int main(){
cin>>l>>r;
while(l%3){
++l;
}
for(i=l;i<=r;i+=3){
cout<<i<<” ”;
}
return 0;
}