需要解释此代码(算法)

时间:2012-08-19 11:43:24

标签: c algorithm math dynamic

问题:

拉里在数学方面非常糟糕 - 他通常使用计算器,这在大学期间运作良好。不幸的是,在滑雪事故发生后,他和他的好伙伴Ryan在一个荒岛上被击中。他们现在正试图花一些时间搞清楚一些好问题,如果他不能回答,Ryan会吃Larry,所以他的命运取决于你!

这是一个非常简单的问题 - 给定数字N,K数小于N的方式可以加到N?

例如,对于N = 20和K = 2,有21种方式:

0+20
1+19
2+18
3+17
4+16
5+15
...
18+2
19+1
20+0

输入 每一行将包含一对数字N和K.N和K都是1到100的整数,包括1和100。输入将在2 0结束。 产量 由于拉里只对答案的最后几位感兴趣,因此对于每对数字N和K,在一行上打印单个数字mod 1,000,000。

示例输入

20 2
20 2
0 0

示例输出

21
21

解决方案代码:

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
#define maxn 100

typedef long ss;
ss T[maxn+2][maxn+2];

void Gen() {
    ss i, j;
    for(i = 0; i<= maxn; i++)
        T[1][i] = 1;
    for(i = 2; i<= 100; i++) {
        T[i][0] = 1;
        for(j = 1; j <= 100; j++)
            T[i][j] = (T[i][j-1] + T[i-1][j]) % 1000000;
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    ss n, m;
    Gen();
    while(cin>>n>>m) {
        if(!n && !m) break;
        cout<<T[m][n]<<endl;
    }
    return 0;
}

这个计算是如何推导出来的? 它是如何来的T[i][j] = (T[i][j-1] + T[i-1][j])

4 个答案:

答案 0 :(得分:1)

注意 :我只使用 n k (小写)来引用一些匿名变量。我总是使用N和K(大写)来表示问题中定义的N和K(总和和部分的数量)。

让C( n k )成为 n 选择 k 的结果,然后解决方案问题是C(N + K - 1,K - 1),假设那些K数是非负的(或者即使对于N = 0和K = 2,也会有无限多的解)。

由于K数是非负的,并且总和N是固定的,我们可以将问题看作:在K人中划分糖果的方法有多少。我们可以将糖果分成一条线来分开糖果,并在糖果之间放置(K-1)分离器。 (K-1)分离器将糖果分成K部分的糖果。从另一个角度来看,它也像在(N + K - 1)位置中选择(K - 1)位置放入分隔符,然后其余的位置是糖果。因此,这解释了为什么方式的数量是N +(K-1)选择(K-1)。

然后问题减少到如何找到C的最低有效位( n k )。 (由于maxn中定义的N和K的最大值为100,我们不必担心算法是否达到O(n 3 )。


计算使用此组合身份C( n k )= C( n - 1 k )+ C( n k - 1 )(Pascal's rule)。实现的聪明之处在于它不存储C( n k )(组合结果表,这是一个锯齿状数组),但它存储换句话说是C(N,K)。身份实际上存在于T[i][j] = (T[i][j-1] + T[i-1][j])

  • 第一个维度实际上是K,即部分的数量。第二个维度是和N. T[K][N]将直接存储结果,并根据上面得出的数学结果,是C(N + K - 1,K - 1)的(最低有效数字)。 / LI>
  • T[i][j] = (T[i][j-1] + T[i-1][j])重写为等效的数学结果:

    C(i + j-1,i-1)= C(i + j-2,i-1)+ C(i + j-2,i-2),根据同一性是正确的。

程序将逐行填充数组:

  • 使用静态数组初始化为0的事实,行K = 0已经初始化为0。
  • 它用1填充行K = 1(只有1种方法将N分成1部分)。
  • 对于其余的行,它将N = 0设置为1(只有一种方法将0分成K部分 - 所有部分都是0)。
  • 然后其余部分填充表达式T[i][j] = (T[i][j-1] + T[i-1][j]),它将引用前一行,以及同一行的前一个元素,这两个元素都已在之前的迭代中填充。

答案 1 :(得分:0)

这是一个着名的问题 - 您可以查看解决方案here

将N个相同球放入K盒的方法有多少。

以下算法是针对您的问题的动态编程解决方案:

将D [i,j]定义为我的数字小于j的方式的数量,可以总结为j。

0&lt; = i&lt; = N. 1&lt; = j&lt; = K

每个j的D [j,1] = 1。

其中j&gt; 1你得到:

D[i,j] = D[i,j-1] + D[i-1,j-1] +...+ D[0,j-1]

答案 2 :(得分:0)

设C(x,y)为x的结果选择y,然后T[i][j]的值等于:C(i - 1 + j, j)

你可以通过归纳来证明这一点。

基本情况:

T[1][j] = C(1 - 1 + j, j) = C(j, j) = 1

T[i][0] = C(i - 1, 0) = 1

对于诱导步骤,使用公式(对于0 <= y <= x):

C(x,y) = C(x - 1, y - 1) + C(x - 1, y)

因此:

C(i - 1 + j, j) = C(i-1+j - 1, j - 1) + C(i-1+j - 1, j) = C(i-1+(j-1), (j-1)) + C((i-1)-1+j, j)

或换句话说:

T[i][j] = T[i,j-1] + T[i-1,j]

现在,正如之前提到的 nhahtdh ,您要寻找的值是C(N + K - 1,K - 1) 等于:

T[N+1][K-1] = C(N+1-1+K-1, K-1)

(模数1000000)

答案 3 :(得分:0)

该问题被称为“整数分区问题”。基本上存在n的k分区的递归计算,但是你的解决方案只是它的动态编程版本(非递归和自下而上的计算)。