版权声明:私藏源代码是违反人性的罪恶行为!博客转载无需告知,学无止境。 https://blog.csdn.net/qq_41822235/article/details/82109081
目录
一、 解法一
二、 解法二
三、 解法三
四、 解法四(换元法)
五、 解法五
要求计算出9~16的和,写出正确答案是100。究竟有多少种连续的正数序列的和为100(至少包括两个数)。另一组连续正数和为100的序列:18,19,20,21,22。现有问题如下,能不能也很快的找出所有和为S的连续正数序列?
输出所有和为S的正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
一、 解法一
数学知识偏多一点,但也不是难到无法求解,稍微麻烦一点就是了。首先我们需要回答三个问题。
n = 2k + 1时,n项连续正数序列的和为S的条件: n & 1 && S / n == 0 解读 逻辑与的左边要求n为奇数,右边要求整个序列的平均数恰好为中间数。
n = 2k时,n项连续正数序列的和为S的条件: S % n * 2 == n 解读 S % n 的结果是中间两项左边的那项,乘2刚好是项数。举例,现有S = 39,6个连续正数序列和式能不能为S呢?套用公式,39 % 6 * 2 =6 == 6,我们也知道,这次的序列是 4、5、6、7、8、9,取余的结果为3对应着值为6的那一项,也就是中间项左边的那一项。
和为S,项数为n,如何写出这个序列? S / n - (n-1) / 2 解读 执行的除法是地板除法(floor),不管最终结果有无小数都直接舍去。仍使用上述例子,39 / 6 = 6,6恰好是中间项左边的那一项,6 - (6-1)/ 2 = 4,恰好是序列最左端。序列写出来就没问题。
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > allRes;
for (int n = sqrt(2 * sum); n >= 2; --n) {
if (((n & 1) == 1 && sum % n == 0) || (sum % n * 2 == n)){
vector<int> res;
//j用于计数,k用于遍历求值
for(int j = 0,k = sum / n - (n - 1) / 2; j < n; j++, k++)
res.push_back(k);
allRes.push_back(res);
}
}
return allRes;
}
};
还有一点需要注意,S = (a0 + a0 +n-1)* n / 2,等差序列公式自然是不必说的。对其进行放缩,就有S > n平方 / 2;即n < 根号2S(这一点在解法4中也有涉及)。这样做的话可以减少遍历次数。 在for循环中就有体现。
for (int n = sqrt(2 * sum); n >= 2; --n)
时间复杂度为O(根号sum )。
二、 解法二
暴力求解,类似于TCP的滑动窗口协议。
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
//至少包含2个数
vector<vector<int> > allRes;
int low = 1;
int high = 2;
while(high > low){ //终止条件,因为题干要求至少2个数
//连续且差为1的序列,求和公式可知
int cur = (low + high) * (high - low + 1) / 2;
if(cur < sum) //当前窗口内的值之和小于sum,右框右移一下
high++;
else if(cur == sum){ //相等,就将窗口内所有数添加进结果集
vector<int> res;
for(int i = low; i <= high; ++i)
res.push_back(i);
allRes.push_back(res);
low++;
}
else //如果当前窗口内的值之和大于sum,左窗框左移一下
low++;
}
return allRes;
}
};
三、 解法三
同样是受到了TCP滑动窗口协议的启发。还有迭代的想法在里面,也可以是说状态转移方程。
用begin和end分别表示序列的左值和右值,首先将begin初始化为1,end初始化为2;
若[begin, end]之和 > S,从序列中去掉第一个值(增大begin);
若和 < S,增大end,和中加入新的end;
等于S,将[begin, end] 纳入到结果集中;
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > allRes;
if(sum < 3) return allRes; //至少都要有2项
int mid = (sum + 1) >> 1; //向上取整,故而begin只能逼近而不能取到
int begin = 1;
int end = 2;
int cur = begin + end;
while(begin < mid)
{
while(cur > sum){ //这个循环的隐患在于循环结束时,beign == end而且和等于sum
cur -= begin;
begin++;
}
if(cur == sum && begin < end) //防止出现某个数组只有1项的情况
InsertRes(begin, end, allRes);
end++;
cur += end;
}
return allRes;
}
void InsertRes(int begin, int end, vector<vector<int>> &allRes){
vector<int> temp;
for(int i = begin; i <= end; ++i){
temp.push_back(i);
}
allRes.push_back(temp);
}
};
四、 解法四(换元法)
根据所学知识,有:
(a + b)(b - a + 1) = 2 * sum;此为等差数列公式。
令i = b - a + 1(项数), j = a + b(首末项之和);现讨论取值范围。i >= 2(序列中至少有2项), j >= 3(序列之和至少为3);隐藏的关系是: j > i同时还有 i * j = 2 * sum,进行放缩之后就有 i * i < 2 * sum,即 i < 根号(2 * sum)。对i进行遍历,找出i,j∈正整数且j - i + 1为偶的取值。
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > allRes;
if(sum < 3)
return allRes;
int twofoldSum = 2 * sum;
int i,j;
for(i = sqrt(twofoldSum); i >= 2; --i) //枚举出合适的项数
{
if(twofoldSum % i == 0) //首末项之和为整数
{
j = twofoldSum / i; //求出首末项之和
int temp = j - i + 1; //解方程得到2倍的左值等于j - i + 1;要求左边界为整数
if (!(temp & 1)) //要求temp是偶数
{
int begin = temp >> 1;
int end = j - begin; //j的实际意义是首末项之和
vector<int> res;
InsertRes(begin, end, res);
allRes.push_back(res);
}
}
}
return allRes;
}
void InsertRes(int begin, int end, vector<int> &res)
{
for(int i = begin; i <= end; ++i)
res.push_back(i);
}
};
五、 解法五
突破口仍然是先求出项数,然后求出序列第一项;与解法一相差不大。但具体细节上还有较大差异。
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > allRes;
if(sum < 3)
return allRes;
for(int k = sqrt(2 * sum); k >= 2; --k) //k为项数
{
double cheat = (2.0 * sum / k - k + 1) / 2.0; //为了验证首项是否为整数
int begin = cheat;
if(begin == cheat) //首项确实为整数
{
vector<int> res;
for(int i = begin; i < begin + k; ++i)
res.push_back(i);
allRes.push_back(res);
}
}
return allRes;
}
};
---------------------
作者:时代跑得太快
来源:CSDN
原文:https://blog.csdn.net/qq_41822235/article/details/82109081
版权声明:本文为博主原创文章,转载请附上博文链接!