一道笔试题

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个数。如果路过的大神知道,还望不吝赐教。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注