题目描述:一个正整数有可能可以被表示为 n(n >= 2) 个连续正整数之和,如:
15=1+2+3+4+5
15=4+5+6
15=7+8
请编写程序,根据输入的任何一个正整数,找出符合这种要求的所有连续正整数序列。
输入数据:一个正整数,以命令行参数的形式提供给程序。
输出数据:在标准输出上打印出符合题目描述的全部正整数序列,每行一个序列,每个序列都从该序列的最小正整数开始、以从小到大的顺序打印。如果结果有多个序列,按各序列的最小正整数的大小从小到大打印各序列。此外,序列不允许重复,序列内的整数用一个空格分隔。如果 没有符合要求的序列,输出 “NONE” 。
例如,对于 15 ,其输出结果是:
1 2 3 4 5
4 5 6
7 8
对于 16 ,其输出结果是:
NONE
评分标准:程序输出结果是否正确。
解法一:最简单的想法,双重for循环。伪代码:
for(int i=1; i < n/2; i ++) int sum =0; for(int j=i; j < n/2; j ++) sum += j; if(sum > n) break; else if(sum == n) print();
时间复杂度o(n^2).
解法二:确定a1以后,我们知道只要确定ap就可以利用通项公式(a1 + ap) * (ap – a1 +1) / 2求和.那么为了确定ap,可以采用二分查找法的方式。伪代码:
for(int i=1; i < n/2; i ++) { int start = i; end = n/2; int mid = 0; while(start <= end) { mid = (start + end)/2; sum = (i + mid) * (mid – i +1) / 2; if(sum > n) end = mid -1; else if(sum < n) start = mid +1; else print(); } }
时间复杂度为o(nlog(n))
解法三:sum = (a1 + ap) * (ap – a1 +1) / 2 = ( a1 + ap) / 2 * (ap – a1 +1) = ak * m 。其中ak表示这个等差数列中间那个数,m表示个数而已。因此只要确定了中间那个数就可以了。
中间数的可能性有两个,一个就是a1, a2, …… ap为奇数个,则中间那个数就是ak。若为偶数,则中间那个数为ak,ak+1。伪代码:
//i=1 可以提高到sqrt(n/2),大家可以去验证一下,但是速度好像不怎么提高 for(int i=sqrt(n/2); i < n/2; i ++) { if(n % i == 0) { int m = n / i; if(i > m/2 && m&1==1) //必须为奇数 { print(); } } else if(n %( i + i +1) == 0) { int m = n / (i+i+1); if(i>=m) print(); } }
时间复杂度为o(n)。
解法一源代码如下:
#include <iostream> #include <Windows.h> #include <fstream> using namespace std ; ifstream fin("baidu0501.in"); ofstream fout("baidu0501.out"); int index =1; int cas; void search() { long long n; long long sum=0; //累加 long long flag=0,tag=0; long t_s = GetTickCount(); fin >> n; tag = 0; for(int i=1;i<=n/2;i++) { ////////////////////// flag=0;sum=0;//清零,很重要 for(int j=i;j<=n/2+1;j++) { sum+=j; if(sum==n) { flag=1; tag=1; for(int k=i;k<=j;k++) {fout<<k<<" ";} } if (sum > n) { break; } } ////////////////////// if(flag==1) { fout<<endl; } ////////////////////// } if(tag==0) { fout<<"NONE"<<endl; } long t_e = GetTickCount(); fout <<"Time:" << t_e- t_s << endl; } void main() { fin >> cas; while (index <= cas) { search(); index ++; } }
解法二源代码如下:
#include <iostream> #include <fstream> #include <Windows.h> #include <math.h> #include <stdlib.h> using namespace std; ifstream fin("baidu0501.in"); ofstream fout("baidu0501.out"); int cas; int index = 1; long long n; bool flag; void print(int s, int e) { for (int i=s; i <= e; i ++) { fout << i << " "; } fout << endl; } //二分法求解 void bisearch() { long long half = (n>>1) + 1; long long start, end, mid; long long sum; n = n << 1; for (long long i=1; i < half; i ++ ) { start = i +1; end = half; mid = (start + end) >> 1; while (start <= end) { sum = ((i + mid) * (mid - i + 1)); if (sum > n) { end = mid -1; } else if(sum < n) start = mid + 1; else { flag = true; for (long long k=i; k <= mid; k ++) { fout << k << " "; } fout << endl; break; } mid = (start + end) >> 1; } } } int main() { fin >> cas; while (index <= cas) { long s_t = GetTickCount(); flag = false; fin >> n; fout << "Case " << index << ":" << endl; bisearch(); if (!flag) { fout <<"NONE" << endl; } index ++; long s_e = GetTickCount(); fout << "Time:" << s_e - s_t << endl; } return 0; }
解法三源代码如下:
#include<iostream> #include<fstream> #include <Windows.h> #include <math.h> using namespace std; ifstream fin("baidu0501.in"); ofstream fout("baidu0501.out"); int cas, index = 1; int n; void center() { long t_s = GetTickCount(); fout << "Case "<<index <<":" << endl; int half = n >> 1; bool flag = false; for (int i=sqrt((float)half); i <= half; i ++) { if (n % i == 0) { int m = n / i; if ( m&1==1 && i >= (m>>1) ) { int l = i+(m>>1); for (int j = i-(m>>1); j <= l; j ++) { fout << j << " "; } fout << endl; flag = true; } } else if (n % (i + i +1) == 0) { int m=n / (i + i +1); if ( i >= m) { int l = i + m; for (int j =i-m+1; j <=l; j++ ) { fout << j << " "; } fout << endl; flag = true; }//end if i }//end else } if (!flag) { fout <<"NONE" << endl; } long t_e = GetTickCount(); fout<<"Time : " << t_e - t_s << endl; } int main() { fin >> cas; while(index <= cas) { fin >> n; center(); index ++; } return 0; }