题目的意思并不难理解:输入两个整数M,NN, M( 1 <= N, M <= 1000000000),如果在范围[1,M]内连续整数的和为N,按从小到大次序输出所有这样的连续段,当输入的M,N都为0时结束。
譬如:
Sample Input
20 10
50 30
0 0
Sample Output
[1,4]
[10,10]
[4,8]
[6,9]
[9,11]
[30,30]
因为M,N的范围比较大,一般的算法很容易超时,所以在这里,时间效率尤其重要。
方法一:回溯暴力求解
该方法是看到题目后最容易想到的,运行结果也正确,但提交时,严重超时。
代码如下:
后来将两个for循环语句都再增加一条限制:i<=m/2和j<=m/2 结果还是一样。
Time Limit Exceeded | 2058 | 1000MS | 248K | 349 B | C+ |
方法二:求段长法
该方法来自严明超同学,我在这里稍微修改了一条语句,核心思想是利用等差数列求和公式:(d+1)*d/2+d*(a-1)=n,其中d为段长,a为等差首项,n为d项连续整数数列的和。
因为一开始段长要最大,所以a取1,根据该公式可求得:d=(int)(sqrt(0.25+2*n)-0.5)。然后段长d逐步减1(即d--),利用公式a=(2*n/d-d+1)/2 分别求得首项a的值,并用a==(int)a判定首项a是否为整数,如果是则输出结果,不是则跳过,然后d--,当d=0时,跳出循环,打印回车,结束本次求解。
代码如下:
Accepted | 2058 | 62MS | 256K | 450 B | C++ |
AC成功,时间为62MS。
方法三:公式推理法
具体思路是:
1、假设连续的整数段为 (x+1)+(x+2)+(x+3)+...+(x+i) 其中x=0,1,2,3....
2、设N=(x+1)+(x+2)+(x+3)+...+(x+i) => N-(1+2+3+..+i)=x*i 化解后得:N-(i+1)*i/2=x*i
3、因为x要为整数,所以得出最终判定条件:(N-(i+1)*i/2)%i==0
4、根据2求得x=n/2-(1+i)/2
5、则连续整数段第一项x+1=n/i-(i-1)/2 最后项x+i=n/2+i/2
6、又因为x+1>=0 x+i<=M 则n/i-(i-1)/2>=0,n/2+i/2<=M分别作为for循环的条件,经过测试位置不可互换,也可以根据数学推理来论证两个条件该如何选择。
代码如下:
Accepted | 2058 | 46MS | 184K | 443 B | C |