Problem Description
Given a sequence 1,2,3,......N, your job is to calculate all the possible sub-sequences that the sum of the sub-sequence is M.
Input
Input contains multiple test cases. each case contains two integers N, M( 1 <= N, M <= 1000000000).input ends with N = M = 0.Output
For each test case, print all the possible sub-sequence that its sum is M.The format is show in the sample below.print a blank line after each test case.Sample Input
20 1050 30
0 0
Sample Output
[1,4][10,10]
[4,8]
[6,9]
[9,11]
[30,30]
分析:这一题的题目我也理解了很久才能明白,一开始还要借助翻译器来翻译。我还以为他是一个数组问题,结果还是一个连续区间求和。而第一个数N就是你能用的数字的最大范围【0,N】 而M就是你要求的一个总和。
第一次求解的时候我打算用暴力方法直接穷举列举所有的可能情况。但是直接给我一个超时。这里我附上我第一次超时的代码,它的时间复杂度最高可以到O(M*M)
#include<iostream>
#include<math.h>
using namespace std;
int main ()
{
double n,m;
long i,j;
while (cin>>n>>m, n||m )
{
double k;
for ( i=1;i<=m;i++)
{
for ( j=i;j<=(m/i)*i;j++)
{
k=2*m/(j-i+1)-i;
if (k==j)
{
printf("[%d,%.0f]\n",i,k);
break;}
}
}
}
return 0;
}
但是这种穷举方法几乎没有任何技巧可言,就算你怎么优化,时间复杂度依旧是两数的乘机。
#include<iostream>
#include<math.h>
using namespace std;
int main ()
{
double n,m;
long i,j;
while (cin>>n>>m, n||m )
{
double k;
for ( i=1;i<=m;i++)
{
for ( j=i;j<=(m/i)*i;j++)
{
k=2*m/(j-i+1)-i;
if (k==j)
{
printf("[%d,%.0f]\n",i,k);
break;}
}
}
}
return 0;
}
那么。问题来了!!我们必须要把复杂度变成o(m)以下。怎么解决?????
普及一下,前N项和的公式Sn=(a1+an)*n/2
则2Sn=(a1+an)
在这里,有一个很必要的逻辑思维。我们假设首项为a1,尾项下标与首项相差不能超过根号(2Sn)
为什么?????
因为 2Sn=(a1+an) *n=(2a1+(n-1)*n )<(n-1)*n <(n-1)*(n-1)
所以,我们可以求出
(1).下标与首项相差的数值k,
(2).反用前N项求和公式求出第一项a1,
关键要注意的是,第一步我们求出的那个K值仅仅是首尾两项的差值,要表达尾项的数值大小,必须要用k+a+1表达
#include<iostream>
#include<math.h>
using namespace std;
int main ()
{
double n,m;
long i,j;
while (cin>>n>>m, n||m )
{
double k,a;
for ( k=int(sqrt(2*m));k>=1;k--)
{
a=m/k-(k-1)/2;
if (a==int(a)) printf("[%.0f,%.0f]\n",a,k+a-1);
}
cout<<endl;
}
return 0;
}
可以看到,这次我们的时间复杂度只有O(sqrt(2*m),远远优于第一个。
sqrt(2*m)<pre name="code" class="cpp" style="font-size: 24px; font-weight: bold;">sqrt(2*m)