1. 程式人生 > >UVA 10943 How do you add 組合數學之隔板法 OR DP

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