HDU 2058 The sum problem

来源:互联网 发布:边际替代率递减知乎 编辑:程序博客网 时间:2024/04/28 14:59


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 10
50 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;}

但是这种穷举方法几乎没有任何技巧可言,就算你怎么优化,时间复杂度依旧是两数的乘机。

那么。问题来了!!我们必须要把复杂度变成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)
2 0
原创粉丝点击