1. 程式人生 > >1312 連續自然數和

1312 連續自然數和

題目描述 Description

對於一個自然數M,求出所有的連續的自然數段,使得這些連續自然數段的全部數字和為M.
eg:1998+1999+2000+2001+2002=10000,所以從1998到2002的一個自然數段為M=10000的一個解。 

輸入描述 Input Description

一個數M

輸出描述 Output Description

每行兩個數,為連續自然數段的一頭一尾,所有輸出行的第一個數按照升序排列

樣例輸入 Sample Input

10000

樣例輸出 Sample Output

18 142

297 328

388 412

1998 2002

思路:

列舉上屆,下屆通過二分搜尋來確定。如果下屆加上屆這段範圍內的值超過輸入數M,則縮小上屆。如果小於輸入的數M則擴大上屆。

最後返回結果。

#include<cstdio>
#include<algorithm>
using namespace std;

// 求前n項和 
inline unsigned nsum(unsigned s,unsigned e)
{
	unsigned sum = 0;
	unsigned n = (e - s + 1); // 項數 
	if(!(n%2))  // 整除 
	    sum = n/2 * (s + e);
	else
		sum = (s + e)/2 * n;
	return sum;
}
// 二分查詢 
unsigned bsearch(unsigned start,unsigned end,unsigned key)  // key是最終的和 
{
	unsigned mid = 0;
	unsigned s = start;
	unsigned v = 0;
	while(start < end)
	{
	    mid = (start + end)/2;
		v = nsum(s,mid);
		if(v == key)   
			return mid;  // 返回搜尋的上屆
		else if(v > key)	
			end = mid - 1;
		else if(v < key)
			start = mid + 1;   // 在start和end的範圍內找 目標end 
	}	
	mid = (start + end)/2;
	v = nsum(s,mid);
	return v == key ? mid : -1;  // 返回mid 
}

// key傳入的應該是上界 
int main()
{
	unsigned m;
	unsigned s,e,sum;
	scanf("%d",&m);
	for(int i = 1; i <= m/2+1; i++)
	{
		unsigned end = bsearch(i,m/2+1,m); // 返回下屆
		if(end != -1)
			printf("%d %d\n",i,end);
	}
	return 0;
}