1. 程式人生 > >HDU2058-The sum problem

HDU2058-The sum problem

HDU2058-The sum problem

題目:
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]

這道題一開始使用暴力回溯法,結果顯示Time Limit Exceeded,後來吧兩個for迴圈的條件改為m / 2,結果還是Time Limit Exceeded。(此時演算法複雜度為o(m^2))

後來經過使用求根公式,想通過初始位置求出末位置,期間只用到一個for迴圈,演算法複雜度為o(m),結果還是Time Limit Exceeded。(當時心裡真的崩潰了。。。)

程式碼如下(評測下來Time Limit Exceeded):

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n,m,sum;
    while(~scanf("%d %d",&n,&m))
    {
        if(!n && !m) break;
        for(int i = 1;i < m / 2;i++)
        {
            if((1 + 8 * m + 4 * i * i - 4 * i) > 0)
            {
                double j = (sqrt(1 + 8 * m + 4 * i * i - 4 * i) - 1) / 2.0;
                if(j - (int)j == 0)
					cout << "[" << i << "," << j << "]" << endl;
				else if(j > m / 2) break;
            }
		}
        cout << "[" << m << "," << m << "]" << endl << endl;
    }
    return 0;
}

之後覺得演算法複雜度還應該更加低才是,那麼只能從for迴圈中i的終止條件來下手了。此時我將i表示為初始位置,j表示範圍的長度。因為該數列是公差為1的等差數列,根據等差數列求和公式可以得出:(i+i+j-1)j/2=m ==>(2i-1+j)j=2m,由此可得j肯定小於等於sqrt(2m)。 然後從長度最長的時候開始列舉,通過區域長度j算出初始位置i,隨後由if語句判斷(還是根據等差數列求和公式進行判斷),滿足的打印出來就可以了。此時的演算法複雜度為o(m^0.5),終於ac。

程式碼如下:

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n,m,sum;
    while(~scanf("%d %d",&n,&m))
    {
        if(!n && !m) break;
        for(int j = sqrt(2 * m);j > 0;j--)   //j代表區域長度
        {
            int i = ((2 * m) / j + 1 - j) / 2;  //i代表區域的初始點
            if(m == ((2 * i + j - 1) * j) / 2) printf("[%d,%d]\n",i,i + j - 1);
        }
        cout << endl;
    }
    return 0;
}