UVA 10943 How do you add 组合数学之隔板法 OR DP

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的数分成m组有多少种
这道题目可以用两种方法去实现,第一种是组合数学,另一种是DP,先说一下组合数学:

题目中给了是n,我们可以这样来想然后去处理:将n分成n个1 然后再进行划分,划分的依据就是组合数学里面的隔板法

比如是10个1 ,让你去分成两个数去组成:

第一步:写出十个一: 1 1 1 1 1 1 1 1 1 1,这样就有10-1=9个空可以插板子,只能是插到中间,且只能是用1个板

第二步:因为有可能由0来组成的,因此需要考虑由多少个数组成就加几个0,这样就是加两个0:1 1 1 1 1 1 1 1 1 1 0 0

为什么是要加两个0?因为有一个板子需要插到这n+k-1个空里面,当板子插到0的时候就像没加0的时候将板子插到外面了,你可以自己枚举一下,当k(就是需要几组数)为3 ,4时 的情况,0的个数如果不是k没有办法插板子;

第三步:

隔板法:C(n+k-1,k-1)= (n+1)(n+2)... (n+k-1),

            计算利用 C(n,m)= C(n-1,m-1)+ C(n-1,m)即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N=5e5+7;
const int INF = 0x3f3f3f3f;
const int MOD=1e6;
ll dp[1005][1005];
void solve(int n,int m)
{
    rep(i,0,n) dp[i][i]=1;
    rep(i,1,n){
        dp[i][0]=1;
    rep(j,1,i){
    dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%MOD;
    }
    }
    printf("%lld\n",dp[n][m]);
}
int main()
{
int n,k;
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    while(scanf("%d%d",&n,&k)!=EOF,n+k){
        solve(n+k-1,k-1);
    }

 return 0;
}

至于DP,那就需要自己推一下状态转移方程了:f(i,j)为 j 拆成 i 个数字的方法数,则有f(i,j)= sum(f(i,k))  { 0 ≤ k ≤ j };

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N=5e5+7;
const int INF = 0x3f3f3f3f;
const int MOD=1e6;
ll dp[1005][1005];//dp[i][j]表示用i组数构成j的种数
void solve()
{
    memset(dp,0,sizeof dp);
    rep(i,0,100)
        dp[1][i]=1;//用一组数构成i,就只存在i本身这一种情况
    for(int i=1;i<=100;i++){
        for(int j=0;j<=100;j++){
            for(int k=0;k<=j;k++){
                dp[i][j]=(dp[i][j]+dp[i-1][j-k])%MOD;
            }
        }
    }
}
int main()
{
    int n,k;
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
        solve();
    while(scanf("%d%d",&n,&k)!=EOF,n+k){
    printf("%lld\n",dp[k][n]);
    }
    
 return 0;
}

下面是不考虑有0组成的情况的方案数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N=5e5+7;
const int INF = 0x3f3f3f3f;
const int MOD=1e6;
ll dp[1005][1005];//dp[i][j]表示用i组数构成j的种数
void solve()
{
    memset(dp,0,sizeof dp);
    rep(i,0,100)
        dp[1][i]=1;
    for(int i=2;i<=100;i++){
        for(int j=1;j<=100;j++){
            for(int k=1;k<j;k++){
                dp[i][j]=(dp[i][j]+dp[i-1][j-k])%MOD;
            }
        }
    }
}
int main()
{
    int n,k;
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
        solve();
    while(scanf("%d%d",&n,&k)!=EOF,n+k){
    printf("%lld\n",dp[k][n]);
    }
    
 return 0;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/84033429
今日推荐