枚举和二分
一切都从头开始,今天先复习枚举和二分。枚举就不多说了,说一下二分,二分其实是分治的一种,是当n=2是的一种情况,也叫二分查找(折半查找),二分查找速度快,比你从头一个一个的查可快多了,二分查找是每次把集合一分为二,看你要找的是在前一半还是后一半,如果在前一半再把前一半一分为二以此类推知道找到你想要的,在后一半同理。二分查找的条件之一是元素表必须是有序表。下面两个模板
1 int l,r,res;//l为左边界,r为右边界,具体值要看目,res为记录mid值的 2 while(l<=r){//循环条件很重要,当l>r时循环结束,res记录的是l=r时的值 3 int mid=(l+r)/2; 4 if(ok(mid)){//ok函数要看题目具体设计 5 l=mid+1;//选哪个要看题 6 //r=mid-1; 7 res=mid; 8 } 9 else 10 r=mid-1; 11 //l=mid+1; 12 }
1 //第一个是整形二分,下面是浮点型二分 2 #define eps 1e-8 //定义宏eps,eps是精度 3 double l,r; 4 while(l+eps<r){ 5 double mid=(l+r)/2.0;//注意除的是2.0 6 if(ok(mid)){ 7 l=mid;//注意没有减一或加一 8 //r=mid; 9 } 10 else 11 r=mid; 12 //l=mid; 13 }
前两天刚做的一个题。题意是给你个整形变量n查找从1~n内的special number个数(0<n<=10000000),special number就是这个数不能有重复的数字出现也不呢不过有前导0,比如023,1124都不是,1234就是。这个题不难但是有时间限制所以关键是看判断是special number 的优化。
1 #include<cstdio> 2 #include<cstring> 3 #define maxn 10000000 4 using namespace std; 5 int a[5000000];///存的是special number 6 ///判断是否是特别数 7 bool ok(int x){ 8 bool vis[10],flag=true;///假设这个数最大有10位,vis的作用就是记录他的每位数是否出现过 9 memset(vis,false,sizeof(vis));//先初始化为没出现过 10 while(x){ 11 int tmp=x%10;///取每位数 12 x=x/10; 13 if(vis[tmp]){ 14 flag=false;///如果这位数曾经出现过则这个数就不是特别数 15 break; 16 } 17 vis[tmp]=true;///否则记录出现过 18 } 19 return flag; 20 } 21 int main(){ 22 memset(a,0,sizeof(a)); 23 int cnt=0; 24 for(int i=1; i<=maxn; i++){///先把所有的特别数按顺序存储在数组里,这就是枚举 25 if(ok(i)) 26 a[cnt++]=i;///cnt+1是所有特别数的个数 27 } 28 int n; 29 while(~scanf("%d",&n)){ 30 int l=0,r=cnt-1,res; 31 if(n<=1){ 32 printf("%d\n",0); 33 continue; 34 } 35 while(l<=r){///分的是下标,因为小于n的特别数个数就是小于n的树中最大特别数的下标+1。(好好想想,因为特别数已经按顺序存储) 36 int mid=(l+r)/2; 37 if(a[mid]<n){ 38 l=mid+1; 39 res=mid; 40 } 41 else 42 r=mid-1; 43 } 44 printf("%d\n",res+1); 45 } 46 }