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