算法訓練 數的劃分

問題描述

將整數n分成k份,且每份不能爲空,任意兩份不能相同(不考慮順序)。
  例如:n=7,k=3,下面三種分法被認爲是相同的。
  1,1,5; 1,5,1; 5,1,1;
  問有多少種不同的分法。

輸入格式

n,k

輸出格式

一個整數,即不同的分法

樣例輸入

7 3

樣例輸出

4 {四種分法爲:1,1,5;1,2,4;1,3,3;2,2,3;}

數據規模和約定

6<n<=200,2<=k<=6

思路

首先這道題是一個跟組合數學有關的題目,我們把它轉化成如下問題:

有n個小球放到k個盒子裏,各個盒子無差別,每個盒子裏必須要有小球,共有幾種放法?

這裏細分成三種情況 :
第一,n>k,有多種情況;
第二,n==k,此時只能有一種劃分方式;
第三,n<k,此時劃分的可能性爲0 ;

此時我們需要找狀態轉移方程,與之前的簡單dp直接把大問題轉化成小問題不同,本題要把大問題分成兩部分,分別轉化爲小問題。這時我們需要討論在此時的放法中是否存在某一個盒子只有一個小球的情況:
1.若存在,則該盒子和該小球並不影響放法的種數,於是dp[i-1][j-1]種可能性

2.若不存在,則每個盒子中都至少有兩個小球,在這種情況下,我們從每個盒子中都拿走一個小球,也不影響放法的種數。於是dp[i-j][j]種可能性

結合起來就是大問題放法的種數,可得狀態轉移方程dp[i][j]=dp[i-1][j-1]+dp[i-j][j];

代碼

#include <iostream>
using namespace std;
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#include<math.h>
long long m[201][7];
//動態規劃
//m[i][j]表示把數字i劃分成j份一共有多少種可能性
//存在遞推方程
//如果存在一份中爲1,那麼減去1可以劃分的可能性是m[i-1][j-1]
//如果不存在一份爲1
//也即是所有份中都大於1,那麼把每一份減去一個1再進行劃分成j份
//可能性是m[i-j*1][j];
int main()
{
    int n;//n表示要劃分的整數,k表示劃分的份數
    int k;
    cin>>n>>k;
    for(int j=2;j<=k;j++){
        //把1劃分成j>1種可能性爲0;
        m[1][j]=0;
    }
    for(int i=1;i<=n;i++)
        m[i][1]=1;//把數字i劃分成1份只有一種可能性
    for(int i=1;i<=n;i++)
        m[i][2]=i/2;//把數字i劃分成2份有i/2種可能性
    //對m[i][j]進行動態規劃
    for(int i=3;i<=n;i++)
    for(int j=2;j<=k;j++){
        if(i>j)//如果i>j,存在下列遞推關係
            m[i][j]=m[i-1][j-1]+m[i-j][j];
        if(i==j)//i==j,把數字j劃分成j份,1111...1,只有一種可能性
            m[i][j]=1;
        if(i<j)//i<j,可能性爲0
            m[i][j]=0;
    }
    //輸出將數字n劃分成k份的可能性數目
    cout<<m[n][k];
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章