Description

小明比较喜欢研究各种各样的数字,有一天他发现了一类数,并将这些数命名为“小明数”,下面是“小明数”的定义:

数字的二进制由连续的k个1和连续的k-1个0组成。

比如:

1(二进制为:1,k=1)

6(二进制为:110,k=2)

120(二进制为:1111000,k=4)

496(二进制为:111110000,k=5)

现在给你一个数字n,求他所有的因子里最大的“小明数”。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10^5)

第2 - T + 1行:每行1个数n。(1 <= n <= 10^5)

Output

共T行每行对应每个测试用例的结果

Sample Input 1 

2
3
992

Sample Output 1

1
496

思路:刚开始没算好时间复杂度所以直接暴力在main函数里面

#include<iostream>
#include<cstring>
using namespace std;

int a[100];
int b[100];
int huansuan(int n,int a[]) {
	int p=0;//一共p位数 
	while(n) {
		a[p]=n&1;
		n>>=1;
		p++;
	}
//	for(int i = p-1; i>=0; i--) {
//		cout<<a[i];
//	}
//	cout<<endl;
	return p;
}
bool ok(int x) {
	if(x==1) return true;
	int p = huansuan(x,b);
	int cnt0=0;
	for(int i = 0; i<p; i++) {
		 if(b[i]==0) cnt0++;
		 else break;
	}
	if(cnt0*2+1!=p) return false;//因为这里要考虑到cnt0==0的情况,也就是想到了x==1的情况了 
	for(int i = cnt0; i<p; i++) {
		if(b[i]!=1) return false;
	}
	return true;
	
}
int main()
{
	int n,t;
	cin>>t;
	while(t--) {
		memset(a,0,sizeof(a));
		cin>>n;
		huansuan(n,a);
		for(int i = n-1; i>=0; i--) {
			if(ok(i)) {
				printf("%d\n",i); 
				break;
			}
		}
	}
	
	return 0 ;
 }

而且没加上是因子的判断,,,很失败、、

TLE之后发现打表,刚开始的打表也是失败的:

//超时做法 
#include<iostream>
#include<cstring>
using namespace std;

int a[100];
int b[100];
int db[100005];

int huansuan(int n,int a[]) {
	int p=0;//一共p位数 
	while(n) {
		a[p]=n&1;
		n>>=1;
		p++;
	}
//	for(int i = p-1; i>=0; i--) {
//		cout<<a[i];
//	}
//	cout<<endl;
	return p;
}
bool ok(int x) {
	if(x==1) return true;
	int p = huansuan(x,b);
	int cnt0=0;
	for(int i = 0; i<p; i++) {
		 if(b[i]==0) cnt0++;
		 else break;
	}
	if(cnt0*2+1!=p) return false;//因为这里要考虑到cnt0==0的情况,也就是自然想到x==1的情况了 
	for(int i = cnt0; i<p; i++) {
		if(b[i]!=1) return false;
	}
	return true;
	
}
void dabiao() {
	db[1]=db[2]=1;
	for(int i = 2; i<=10005; i++) {
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		huansuan(i,a);
		for(int j = i-1; j>=db[i]; j--) {//这样并不能简化运算,复杂度甚至更高了,不像筛法求素数一样,因为这个不是记忆化,
			if(ok(j)) {					//也就是,你虽然k那层for循环更新了db值,但并不一定是最终结果,所以意义不大 
				db[i]=j;				//建议在看一看线性时间筛法求素数中是怎么降低时间复杂度的!那才是真正的记忆化!
				for(int k = i+db[i]; k<=10005; k+=db[i]) {//你这个记忆化卵用没有,只是先找到了较优解,并非最优解所以此处无意义。 
					db[k]=db[i];						//	只有A*算法类似的才需要较优解,其余情况需要记忆的是最优解才有意义。 
				}
				break;
			}
		}
	}
	
}
int main()
{
	int n,t;
	scanf("%d",&t);
	dabiao(); 
	while(t--) {
		scanf("%d",&n);
		printf("%d\n",db[n]);
	}
	
	return 0 ;
 } 

依旧TLE,并且比时间复杂度略高于直接双层for,(ps:其实直接双层for时间复杂度也是o(n^2),虽然内层不是1~100005,但是取平均,,去掉系数,依旧是o(n^2)  

下面是思考后的打表:

#include<iostream>
#include<cstring>
using namespace std;
int db[10];
int cnt=0;
void dabiao() {
	db[0]=1;cnt++;
	db[1]=6;cnt++;//cnt=2;
	int n=6;
	int tmp1,tmp2;
	while(n<=100005) {
		tmp1=n<<2;
		n=db[cnt]=tmp1+( 1<<cnt );//需要加括号!!! 
//		cout<<db[cnt]<<endl;
		cnt++;
	}
	
}
int main()
{
	int n,t;
	scanf("%d",&t);
	dabiao(); 
//	for(int i = 0; i<cnt; i++) {
//		printf("%d\n",db[i]);
//	}
	while(t--) {
		scanf("%d",&n);
		if(n==1) {
			printf("1\n");
			continue;
		} 
		for(int i = cnt-2; i>=0; i--) {//小细节!需要-1!不然就爆零了! 
			if( n>=db[i] && (n%db[i]==0)) {
				printf("%d\n",db[i]);
				break;
			}
		}
	}
	return 0 ;
 } 
 /*
1
6
28
120
496
2016
8128
32640
130816
 */


24ms运行时间、、、还有一点要注意的是,因子的定义!!eg : 6是6的因子!所以

 n>=db[i]

等号不能拉下!!很气、、

总结:先看题啊算一算时间复杂度,这题数据量1e5,所以不打表就至少1e10,肯定TLE,所以打表是肯定的了,再审题,发现不需要像第一版那样去遍历1e5来找符合小明数的;而应该,根据题目小明数的特点,直接位运算,得到若干个小明数,即:你可以直接找出这些小明数(并且一共就不多,就8个),那为什么还要遍历求呢?直接算把次就好了啊,或者直接提前算好然后再数组里直接赋值初始化,极好极好。

另附网络版:用快速幂做的,但是个人感觉没有必要:


#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <string>  
using namespace std;  
int a[100000];  
int cnt = 0;  
int q_pow(int a,int b){  
    int ans = 1;  
    while(b){  
        if(b&1)  
            ans *= a;  
        b >>= 1;  
        a *= a;  
    }  
    return ans;  
}  
void getnum(){  
    int t = 1;  
    int num;  
    do{  
        int tmp = 2 * t - 2;  
        int tmp2 = t;  
        num = 0;  
        while(tmp2){  
            num += q_pow(2,tmp);  
            tmp2--;  
            tmp--;  
        }  
        if(num < 100000) a[cnt++] = num;  
        t++;  
    }while(num < 100000);  
}  
int main(){  
    getnum();  
    for(int i = 0; i<cnt; i++) {
    	cout<<a[i]<<endl;
	} 
    int t,n;  
    scanf("%d",&t);  
    while(t--){  
        scanf("%d",&n);  
        for(int i = cnt-1; i >= 0; i--){  
            if(n % a[i] == 0){  
                printf("%d\n",a[i]);  
                break;  
            }  
        }  
    }  
    return 0;  
} 
/*
1
6
28
120
496
2016
8128
32640
*/