分数的方法数

时间:2015-09-28 08:25:00

标签: algorithm numbers dynamic-programming

给定一个数字N,打印它可以表示为多少种方式

N = a + b + c + d

1 <= a <= b <= c <= d; 1 <= N <= M

我的观察:

For N = 4: Only 1 way - 1 + 1 + 1 + 1

For N = 5: Only 1 way - 1 + 1 + 1 + 2

For N = 6: 2 ways     - 1 + 1 + 1 + 3
                        1 + 1 + 2 + 2

For N = 7: 3 ways     - 1 + 1 + 1 + 4
                        1 + 1 + 2 + 3
                        1 + 2 + 2 + 2

For N = 8: 5 ways     - 1 + 1 + 1 + 5
                        1 + 1 + 2 + 4
                        1 + 1 + 3 + 3
                        1 + 2 + 2 + 3
                        2 + 2 + 2 + 2

So I have reduced it to a DP solution as follows:
 DP[4] = 1, DP[5] = 1;

for(int i = 6; i <= M; i++)
   DP[i] = DP[i-1] + DP[i-2];

我的观察是正确的还是我遗漏了任何东西。我没有任何测试用例可以运行。如果方法正确或错误,请告诉我。

4 个答案:

答案 0 :(得分:9)

这不正确。这是正确的:

DP[n,k]成为将n表示为k数字总和的方式。 然后你正在寻找DP[n,4]

DP[n,1] = 1
DP[n,2] = DP[n-2, 2] + DP[n-1,1] = n / 2
DP[n,3] = DP[n-3, 3] + DP[n-1,2]
DP[n,4] = DP[n-4, 4] + DP[n-1,3]

我只会解释最后一行,你可以马上看到,为什么其他人都是真的。

让我们来看一个n=a+b+c+d的案例。

如果是&gt; 1,然后n-4 = (a-1)+(b-1)+(c-1)+(d-1)DP[n-4,4]的有效总和。

如果a = 1,则n-1 = b+c+dDP[n-1,3]的有效总和。

同样相反:

对于每个有效n-4 = x+y+z+t,我们都有一个有效的n=(x+1)+(y+1)+(z+1)+(t+1)

对于每个有效n-1 = x+y+z,我们都有一个有效的n=1+x+y+z

答案 1 :(得分:3)

不幸的是,你的再现是错误的,因为对于n = 9,解是6,而不是8。

如果p(n,k)是将n分割成k个非零整数部分的方法的数量,那么我们有

ul.nav

因为存在值为1的分区(在这种情况下取​​出该部分会产生n-1的分区为k-1个部分),或者您可以从每个分区中减去1,从而产生n-k的分区。很容易证明这个过程是一个双射,因此再次发生。

<强>更新

对于特定情况k = 4,OEIS告诉我们还有另一个线性递归仅依赖于n:

p(0,0) = 1
p(n,k) = 0 if k > n or (n > 0 and k = 0)
p(n,k) = p(n-k, k) + p(n-1, k-1) 

这种复发可以通过标准方法解决,以获得明确的公式。我写了一个小SAGE script来解决它并得到以下公式:

a(n) = 1 + a(n-2) + a(n-3) + a(n-4) - a(n-5) - a(n-6) - a(n-7) + a(n-9)

OEIS也gives the following simplification

a(n) = 1/144*n^3 + 1/32*(-1)^n*n + 1/48*n^2 - 1/54*(1/2*I*sqrt(3) - 1/2)^n*(I*sqrt(3) + 3) - 1/54*(-1/2*I*sqrt(3) - 1/2)^n*(-I*sqrt(3) + 3) + 1/16*I^n + 1/16*(-I)^n + 1/32*(-1)^n - 1/32*n - 13/288

我尚未验证。

答案 2 :(得分:1)

#include <iostream>

using namespace std;

int func_count( int n, int m )
{

     if(n==m)
        return 1;
     if(n<m)
        return 0;
     if ( m == 1 )
        return 1;
     if ( m==2 )
        return (func_count(n-2,2) + func_count(n - 1, 1));
     if ( m==3 )
        return (func_count(n-3,3) + func_count(n - 1, 2));

     return (func_count(n-1, 3) + func_count(n - 4, 4));
}

int main()
{

     int t;
     cin>>t;
     cout<<func_count(t,4);
     return 0;
}

答案 3 :(得分:0)

我认为函数f(N,m,n)的定义,其中N是我们想要产生的和,m是和中每个项的最大值,n是总和中的项数应该工作。

如果N> 1,则将p(N,m,n)定义为n = 1为0。 m,否则为N.

表示n> 1,f(N,m,n)=对于f(S-t,t,n-1)从1到N的所有t的总和

这表示从右到左设置每个术语。

然后您可以使用此关系解决问题,可能使用memoization。

对于最大n = 4,并且N = 5000,(并且当有0种可能性时巧妙地实现以快速计算出来),我认为这对于大多数目的而言可能足够快地计算。