题目1354:和为S的连续正数序列
-
题目描述:
-
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
-
输入:
-
输入有多组数据。
每组数据仅包括1个整数S(S<=1,000,000)。如果S为负数时,则结束输入。
-
输出:
-
对应每组数据,若不存在和为S的连续正数序列,则输出“Pity!”;否则,按照开始数字从小到大的顺序,输出所有和为S的连续正数序列。每组数据末尾以“#”号结束。
-
样例输入:
-
4 5 100 -1
-
样例输出:
-
Pity! # 2 3 # 9 10 11 12 13 14 15 16 18 19 20 21 22 #
算法分析
第一种方法是通过移动{begin, finish}确定的区间值cursum来进行查找
else if(cursum<s){
finish++;
cursum+=finish;
}else{
cursum-=begin;
begin++;
}
第二种方式则是利用等差数列求和公式。
设定我们找到了开始数值Ai, 和结束数值An,区间数值和为2*S。
An = Ai + (n-1);
(Ai + An)*n = 2*S
==> (Ai + Ai + n-1)*n = 2*S
==> 2*Ai = (2*S / n) + 1 - n;
只要查找 能被 2*S整除的n, 同时 (2*S / n) + 1 - n 为偶数, 即可得到 Ai 再根据n可得到其他数值。
再根据Ai>0
(Ai + Ai + n-1)*n = 2*S
2*S >(n-1)*(n-1)
所以我们从 sqrt(2*s) 到2 进行判断
文中要求只要有两个
源程序
#include <iostream>
#include <stdio.h>
#include <cmath>
using namespace std;
void printNum(int begin,int finish){
for(int i =begin;i<finish;i++)
printf("%d ",i);
printf("%d\n",finish);
}
void judo(int s){
bool isfind = false;
int begin=1;
int finish=2;
int cursum = 3;
while(begin+finish<=s){
if(cursum==s){
isfind=true;
printNum(begin,finish);
cursum-=begin;
begin++;
finish++;
cursum+=finish;
}
else if(cursum<s){
finish++;
cursum+=finish;
}else{
cursum-=begin;
begin++;
}
}
if(!isfind)
std::cout<<"Pity!"<<std::endl;
std::cout<<"#"<<std::endl;
}
void judonewR(int s){
//(a1+an)*n = 2*s
//(2*a1 + n-1)*n = 2*s
bool isfind = false;
int a = 0;
int i = std::sqrt((double)(2*s));
while(i>1){
if(!((2*s)%i)){
a = 2*s/i - i+1;
if(!(a&1)){
isfind = true;
a = a>>1;
printNum(a,a+i-1);
}
}
i--;
}
if(!isfind)
printf("Pity!\n");
printf("#\n");
}
int main() {
int s;
while(std::cin>>s){
if(s<0)
return 0;
judonewR(s);
}
return 0;
}
/**************************************************************
Problem: 1354
User: KES
Language: C++
Result: Accepted
Time:90 ms
Memory:1532 kb
****************************************************************/