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
*/