‘数字求和‘问题的总结

问题1:给定一个正整数N,求出和为N的所有序列a1,a2, .., ak。其中,0< a1 < a2 < .. < ak < N

比如5 = 1+4 = 2+3 

问题2:有正整数 N, 可表示为a1+a2+a3+...+aK=N;并且 0<a1<a2<...<aK; 求,一共有多少种表示法。

(在此,我自我反省下:没经过思考,起初想当然的认为2和1有一样的复杂度,简直太弱了!)

问题1的解法是利用递归回溯。关键在于定义一个f(n,k),其中n表示需要求的和,k表示允许的最大ai。此回溯算法的出口有两个:一个是找到合适的序列;一个是子问题遍历完成。

算法主要代码如下:

void f(int n, int k)
{
        if (n == 0)
        {
         	print_result();
                count++;
                return ;
        }
        int max, min;
        max = (n<k)? n : (k);
        min = sqrt(2*n) - 1;
        min = (min < 1)?1:min;
        for (int i=max; i>=min; --i)
        {
                if ( !(n-i >= 0) )
			continue;
                a[i] = 1;
                f(n-i, i-1);
                a[i] = 0;
        }
}

初始调用是f(N, N-1) //计算和为N的整数序列,ai最大不能超过N-1

 

第二个问题使用动态规划法,时间复杂度是N^2.

主要思想是定义一个f[N+1][N+1],最后计算F[N][N-1].其中F[n][k]表示,和为n,ai最大是k的序列数。

于是,就有

F[n][k] = F[n][k-1] + F[n-k][k-1] (k < n)

F[n][k] = F[n][n-1] + 1 (k>=n)

边界条件F[0][0] = 1;

主要代码如下:

        for (int n=0; n<N+1; n++)
		for (int k=0; k<N+1; k++)
                {
                        if (k==0)
                                f[n][k] = 0;
                        else if (k<n)
                                f[n][k] = f[n][k-1] + f[n-k][k-1];
                        else if (k>=n)
                                f[n][k] = f[n][n-1] + 1;
                        else
        	        {
                                printf("we should never get here!\n");
                                exit(-1);
			}
                }

由于每一步的操作试试O(1)的加法,所以总的running time是O(N^2).

 

感谢OSC的几位朋友, @zzz2012 @中山野鬼 @看能不能改个名 (哥们,打你的名字真不容易,我还特地查了下)

尤其是@zzz2012

结束。

路漫漫其修远兮,吾将上下而求索。

 

 

 

 

 

你可能感兴趣的:(‘数字求和‘问题的总结)