WARNING: unbalanced footnote start tag short code found.
If this warning is irrelevant, please disable the syntax validation feature in the dashboard under General settings > Footnote start and end short codes > Check for balanced shortcodes.
Unbalanced start tag short code found before:
“99-i-6)/4+1);j++) for(k=j+1;k<”
和同学聊天时提到了在新浪面试记中说的那道编程题,这里再描述一遍题目:从1到100中取5个不相同的数,相加小于100,有多少种方法。
给她看了我的方法:
int iprfunc() { int num=0; int i,j,k,m,n; for(i=1;i<18;i++) for(j=2;j<24;j++) for(k=3;k<32;k++) for(m=4;m<47;m++) for(n=5;n<90;n++) if((i+j+k+m+n)<100 && (i<j) && (j<k) && (k<m) && (m<n)){ num++; } return num; }
大神问了循环边界的确定方法,然后给出一个改进的版本:
int func2() { int num = 0; int i,j,k,m; for(i=1;i<18;i++) for(j=i+1;j<24;j++) for(k=j+1;k<32;k++) for(m=k+1;m<47;m++) { int sum = i+j+k+m; if(sum<95 && m<(99-sum)) { num +=99-sum-m; } } return num; }
既然是递增排列,那么循环初始值就不用像我的方法那样定死了,然后是最去掉一层循环,意思是最 后一个数有多少种情况是可以直接算出来的,99-sum得最后一个数的最大值,然后最后一个数一定是从m+1开始,所以又 num=99-sum-(m+1)+1 = 99-sum-m;
跑一下,确实快了很多,1ms以内解决问题,我之前的是要144ms左右。
然后继续看代码,发现循环边界确定的其实是有问题的,之前只是确定了边界的最大值,然而最大值并不是固定不变的,比如i=1时,j可以取到23,但是i=2时,j只能到22了,也就是说循环边界应该动态确定,修改iprfunc(),如下:
int func3() { int num=0; int i,j,k,m,n; for(i=1;i<18;i++) for(j=i+1;j<((99-i-6)/4+1);j++) for(k=j+1;k<((99-i-j-3)/3+1);k++) for(m=k+1;m<((99-i-j-k-1)/2+1);m++) for(n=m+1;n<(99-i-j-k-m+1);n++) if((i+j+k+m+n)<100){ num++; } return num; }
解释一下,i确定之后,最大情况:j+(j+1)+(j+2)+(j+3) = 99-i,所以 j<(99-i-6)/4 + 1,余下的同理。跑一下,2至3毫秒之间。还是比大神的慢一些。
结合大神的思想,去掉一层循环:
int final() { int num=0; int i,j,k,m; for(i=1;i<18;i++) for(j=i+1;j<((99-i-6)/4+1);j++) for(k=j+1;k<((99-i-j-3)/3+1);k++) for(m=k+1;m<((99-i-j-k-1)/2+1);m++) { int sum = i+j+k+m; num += (99-sum-m); //直接算出有最后一个可选数的数量 如 1+2+3+4,第5个数只能从5到89,共85种情况 } return num; }
然后测试一下:
int main(int argc, char* argv[]) { clock_t start,finish; int total; start=clock(); cout<<func()<<endl; finish=clock(); total=(int)(finish-start); cout<<"改进前(执行1次): "<<total<<endl; start=clock(); cout<<iprfunc()<<endl; finish=clock(); total=finish-start; cout<<"改进后(执行1次): "<<total<<endl; start=clock(); for(int i=0;i<1000;i++) func2(); cout<<func2()<<endl; finish=clock(); total=finish-start; cout<<"改进后2(执行1000次): "<<total<<endl; start=clock(); for(int i=0;i<1000;i++) func3(); cout<<func3()<<endl; finish=clock(); total=finish-start; cout<<"改进后3(执行1000次): "<<total<<endl; start=clock(); for(int i=0;i<1000;i++) final(); cout<<final()<<endl; finish=clock(); total=finish-start; cout<<"改进后4(执行1000次): "<<total<<endl; return 0; }
终于比大神的快一些了:
$ ./1-100.exe 455175 改进前(执行1次): 16537 455175 改进后(执行1次): 141 455175 改进后2(执行1000次): 477 455175 改进后3(执行1000次): 2259 455175 改进后4(执行1000次): 149
不知道此类问题有没有通用的算法,比如1到n中取m个数。如果路过的大神知道,还望不吝赐教。
发表回复