1. 程式人生 > >列出一個正整數表示成n(n>=2)個連續正整數之和的所有形式

列出一個正整數表示成n(n>=2)個連續正整數之和的所有形式

一個正整數有可能可以被表示為n(n>=2)個連續正整數之和,如: 15=1+2+3+4+5 15=4+5+6 15=7+8 
請編寫程式,根據輸入的任何一個正整數n,找出符合這種要求的所有連續正整數序列的個數C。 如:對於15,其輸出結果是3:對於16,其輸出結果是:0。

n=56789,  C = 3                        

n=189909,  C =  5                        

n=999,  C= 7

程式設計思想:

這個問題看起來不是很簡單,需要設計一個演算法:
先講數學:
設等差數列:
an=a+(n-1)*d (這裡首項為a,公差d=1,第n項為an,前n項和為sn)
a1=a
an=a+n-1
sn=(a1+an)n/2=(2a-1+n)*n/2
再回到這個程式設計上來:
我們的輸入資料其實就是sn,需要找到以a開始的n個連續的遞增數列使得和為sn。
這裡我們可以用迴圈來判定,給定一個n,sn已知,就可以求出a,如果a為正整數那麼就可以找到等差數列的首項,加上n給定,d=1,那麼就可以寫出這個和式子。

進一步優化提高程式效率:這裡的n無須一直從2開始列舉下去,可以由sn=(2a-1+n)*n/2,所以a=sn/n-n/2+1/2,該式子為遞減函式,n越大,a越小,而a最小為1,故另a=1時可確定n的最大範圍。

令a=1,得二元一次方程(1/2+n/2)*n=sn,即n^2+n-2*sn=0,可得方程兩個根中取較大的根n=0.5*(-1+sqrt(1+8*sn)),從而確定n的最大列舉範圍。

程式碼如下:

#include<stdio.h>
#include<iostream>
#include<math.h>
using namespace std;
int main()
{
	int n,s,an,i,ans,cnt,fg;
	double a1;
	while(scanf("%d",&n)!=EOF)
	{
		ans=0;
		int mm=0.5*(-1+sqrt(1+8*n)); //列舉的最大範圍 
		for(cnt=2;cnt<=mm;cnt++)
		{
			a1=(2*n+cnt-cnt*cnt)*1.0/(2.0*cnt);
			if(int(a1)==a1&&a1>0)
			{
				printf("%d=%d",n,int(a1));
				for(i=1;i<=cnt-1;i++)
				{
					printf("+%d",int(a1+i));
				}
				printf("\n");
				ans++;
			}
		}
		if(ans==0)printf("None!\n");
		else printf("%d\n",ans);
	}	
	return 0;
}