uva—How do you add?


Problem A: How do you add?

Larry is very bad at math - he usually uses a calculator, which worked well throughout college. Unforunately, he is now struck in a deserted island with his good buddy Ryan after a snowboarding accident. They're now trying to spend some time figuring out some good problems, and Ryan will eat Larry if he cannot answer, so his fate is up to you!

It's a very simple problem - given a number N, how many ways can K numbers less than N add up to N?

For example, for N = 20 and K = 2, there are 21 ways:
0+20
1+19
2+18
3+17
4+16
5+15
...
18+2
19+1
20+0


Input

Each line will contain a pair of numbers N and K. N and K will both be an integer from 1 to 100, inclusive. The input will terminate on 2 0's.

Output

Since Larry is only interested in the last few digits of the answer, for each pair of numbers N and K, print a single number mod 1,000,000 on a single line.

Sample Input

20 2
20 2
0 0

Sample Output

21
21
題意:給出兩個數N,K,N代表數的大小,K代表將N拆成幾個數相加,問N對K能有多少種組合?

分析:

法一:典型的遞推DP是有規律的,一開始可能想不通,可以先寫出一些簡單的情況加以總結。

          1 1:1->1

          1 2:0+1,1+0->2

          1 3:0+0+1,0+1+0,1+0+0->3

          2 1:2->1

          2 2:0+2,1+1,2+0->3

          2 3:0+0+2,0+2+0,2+0+0,0+1+1,1+0+1,1+1+0->6

          3 1:3->1

          3 2:0+3,1+2,2+1,3+0->4

          3 3:0+0+3,0+3+0,3+0+0,0+1+2,0+2+1,1+2+0,2+1+0,1+0+2,2+0+1,1+1+1->10

     由上發現好像(2 2)=(1 2)+(2 1),(2 3)=(1 3)+(2 2),(3 2)=(2 2)+(3 1),(3 3)=(2 3)+(3 2)

     即:dp[N][K]=dp[N-1][K]+dp[N][K-1],我們想一下,爲什麼是這樣呢?

     用遞推的思想,當你求(3 3)的時候,實際上就是(3 2)再加一位0,(2 3)在某一位上加1,依次遞推下去......

     因此有,dp[N][K]=dp[N-1][K]+dp[N][K-1]


void init()
{
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;//初始化!!!
    for(int i=1;i<101;i++)//還有i是從1開始的
    {
        dp[0][i]=1;///(3 1)->1
        for(int j=1;j<101;j++)///需要注意的是dp[j][i]的循環而不是dp[i][j],你在求(3 3)的時候保證
///(2 3)和(3 2)都已經求出來了,因此應該是同一位數的先都求出來。
            dp[j][i]=(dp[j][i-1]+dp[j-1][i])%MOD;
    }
}
法二:由上,我們不難想到另一種方法dp[i][j] = sum{ dp[i-1][j-t] } 表示當前這一位填p

           dp[i][j] 表示i個不超過N的非負整數加起來的和爲j的方法數(剛好與上面相反dp[K][N])

void init()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<101;i++)
        dp[i][0]=1;
    for(int i=1;i<101;i++)
        for(int j=1;j<101;j++)
            for(int t=0;t<=j;t++)
                dp[i][j]=(dp[i][j]+dp[i-1][j-t])%MOD;
}

法三:組合數學的思想——隔板法

          關於組合排列的問題,高中經常做到。

          把一個數分成幾個數相加,相當於把該數大小看成球的個數N,然後將球放入K個盒子(可空)中,有幾種放法。

就是要放入k-1個板  ,即C(N+K-1, K-1)

代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 102
#define MOD 1000000
int dp[N][N];
/*void init()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<101;i++)
        dp[i][0]=1;
    for(int i=1;i<101;i++)
        for(int j=1;j<101;j++)
            for(int t=0;t<=j;t++)
                dp[i][j]=(dp[i][j]+dp[i-1][j-t])%MOD;
}*/
void init()
{
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(int i=1;i<101;i++)
    {
        dp[0][i]=1;
        for(int j=1;j<101;j++)
            dp[j][i]=(dp[j][i-1]+dp[j-1][i])%MOD;
    }
}
int main()
{
    int n,k;
    init();
    while(scanf("%d%d",&n,&k),n&&k)
        cout<<dp[n][k]<<endl;
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章