题目链接地址:
题目描述:
小明很喜欢数学,有一天他在做数学作业时,要求计算出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
#
解题思路:
求和等于S的连续正数序列,等价于找一个公差为1和为S的等差数列。于是我想到了用等差数列求和公式:(a1 + an) * n / 2 = S,但是这里有a1,an,n三个变量,于是我就卡在这里了。。。
后来上作者的博客 程序员面试题精选100题(26)-和为n连续正数序列[算法] 看了一下,由于实在想不到好的算法了,就采纳了作者的算法。
算法一
和等于S的连续正数序列至少有两个元素,从序列[1,2]开始遍历。将序列和与S进行比较,分以下3种情况进行讨论:
(1)如果连续序列 [begin,…,end] 的和大于S,就将begin踢出该序列;
(2)如果连续序列 [begin,…,end] 的和小于S,就将end+1加入到该序列构成新的连续序列 [begin,…,end,end + 1];
(3)如果连续序列 [begin,…,end]的和等于S,就输出当前序列的和,并且将begin踢出该序列;
(4)重复(1),(2),(3)步骤,因为连续正数序列至少有两个元素,所以begin的最大值是S / 2。
举个栗子,如果S = 5,则求和等于S的连续正数序列的过程如下:
1)令begin = 1,end = 2,此时连续序列是[1,2],序列和是3,因为3 < S,所以将end的下一个元素3加入到当前序列中,新的连续序列是[1,2,3];
2)当前连续序列[1,2,3]的和是6,因为6 > S,所以将元素1踢出当前连续序列,新的连续序列是[2,3];
3) 当前连续序列[2,3]的和是5,因为5 = S,所以[2,3]是满足题意的连续序列,输出该序列后,将2踢出序列,新的连续序列是[3];
4) 当前连续序列[3]的begin是3,因为3 > S/2,所以终止遍历操作。
AC代码如下:
// 第一种解法
#include<stdio.h>
/**
* 打印和等于S的连续序列
* @param begin 连续序列的开始值
* @param end 连续序列的终止值
* @return void
*/
void printContinueSequence(int begin,int end)
{
int i = begin;
printf("%d",i);
for(i = begin + 1;i <= end;i++)
{
printf(" %d",i);
}
printf("\n");
}
/**
* 获取和等于S的连续序列
* @param S 用户输入的S
* @return numberOfSequence 和等于S的连续序列的个数
*/
int getContinueSequenceEqualsS(int S)
{
int numberOfSequence = 0;
int begin = 1; // 连续序列的起点
int end = 2; // 连续序列的终点
int middle = S/2; // 因为连续序列的长度最少为2,而begin + end <= S,所以begin最大的值为S/2
int sum = begin + end; // sum是连续序列[begin,end]的和
while(begin <= middle)
{
if(sum == S) // 如果当前序列和等于S,则输出连续序列[begin,end]
{
printContinueSequence(begin,end);
sum = sum - begin;
begin++;
numberOfSequence++;
}
else if(sum < S) // 如果当前的连续序列和小于S,则将end的下一个元素添加到sum中
{
end++;
sum += end;
}
else // 如果当前的连续序列和大于S,则将begin踢出连续序列
{
sum = sum - begin;
begin++;
}
}
return numberOfSequence;
}
int main()
{
int S;
int number;
while(EOF != scanf("%d",&S) && S >= 0)
{
number = getContinueSequenceEqualsS(S);
if(0 == number)
{
printf("Pity!\n");
}
printf("#\n");
}
return 0;
}
/**************************************************************
Problem: 1354
User: blueshell
Language: C++
Result: Accepted
Time:1060 ms
Memory:1020 kb
****************************************************************/
算法二
后来在论坛讨论区 【1354】和为s的连续正数序列 看了3楼的解法后,顿时感觉到了算法的魅力所在。
下面简单介绍一下大牛的算法:
假设和为S的连续序列为[a1,a2,……,an],则可以的到S = a1+a2+…+an,
即S = (a1 + an) * n / 2,这又是等差数列的求和公式,我之前卡在这个公式的原因是没有想到an = a1 + n – 1 这个公式。
由S = (a1 + an) * n / 2和an = a1 + n – 1可以推导出
S = (a1 + a1 + n - 1) * n / 2
===> 2 * a1 + n – 1 = 2 * S / n
===> 2 * a1 = 2S/n – n + 1
===> a1 = (2S/n – n + 1) / 2
因为a1是正整数,而当a1是正整数时,可以得知(2S/n – n + 1) / 2和2S/n也都是正整数。
因为2*a1 > 0,所以2S/n – n + 1 > 0 ===> 2S/n – n >= 0 ===> n * n <= 2S
===> n <= sqrt(2S),
因为和等于S的连续正数序列至少有两个元素,所以n >= 2。
因此算法的流程就是:依次遍历每一个n(2 <= n <= sqrt(2S)),如果n的值能够满足2S/n和a1 = (2S/n – n + 1) / 2同时为正整数,则以a1为首元素,长度为n的连续正数序列的和就等于S。
AC代码如下:
// 第二种解法
#include<stdio.h>
#include<math.h>
/**
* 打印和等于S的连续序列
* @param begin 连续序列的开始值
* @param end 连续序列的终止值
* @return void
*/
void printContinueSequence(int begin,int end)
{
int i = begin;
printf("%d",i);
for(i = begin + 1;i <= end;i++)
{
printf(" %d",i);
}
printf("\n");
}
/**
* 获取和等于S的连续序列
* @param S 用户输入的S
* @return numberOfSequence 和等于S的连续序列的个数
*/
int getContinueSequenceEqualsS(int S)
{
int numberOfSequence = 0;
int n; // 和等于S的连续序列的长度
int a1; // 和等于S的连续序列的第1项
for(n = sqrt(2 * S); n >= 2; n--) // n越大,a1越小,因此n的初始值为sqrt(2 * S),最终值为2
{
if((2 * S) % n == 0) // 保证2S / n是整数
{
if(0 == (2 * S / n - n + 1) % 2) // 保证a1是整数
{
a1 = (2 * S / n - n + 1) / 2;
numberOfSequence++;
printContinueSequence(a1,a1 + n - 1);
}
}
}
return numberOfSequence;
}
int main()
{
int S;
int number;
while(EOF != scanf("%d",&S) && S >= 0)
{
number = getContinueSequenceEqualsS(S);
if(0 == number)
{
printf("Pity!\n");
}
printf("#\n");
}
return 0;
}
/**************************************************************
Problem: 1354
User: blueshell
Language: C++
Result: Accepted
Time:80 ms
Memory:1032 kb
****************************************************************/