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;
}