整數划分問題 動態規划


ACM,OI等比賽,整數划分為常見的入門題,許久沒打比賽,最近做筆試題突然碰到,磕磕絆絆了很久才搞清楚,現在做個筆記。

-------------------------------------------------------------------------------------

題目可見hduoj1028 , 簡單的講:

數字N,整數划分的組合數為多少。整數划分表示正整數的和集為N,比如N=4時:

  4 = 4;
  4 = 3 + 1;
  4 = 2 + 2;
  4 = 2 + 1 + 1;
  4 = 1 + 1 + 1 + 1;

有這5中情況,所以N=4的整數划分的組合數為5。

-------------------------------------------------------------------------------------

解題,開始DP,先對DP進行狀態的設計:

思考的第一步: 考慮到是組合,不能出現重復的情況,比如4=3+1與4=1+3是一種,所以采用“划分數中的最大值”來限定重復狀態。

思考的第二步:狀態設計:dp[n][n]表示“和為n、最大划分數為m”時,組合的數量。

思考的第三步:狀態轉移:由划分數中是否包含m分為兩種狀態,當前組合數等於這兩種狀態下的組合數之和。

  1. n=1時,無論m等於多少,都只有一種{1}的划分,dp[1][m] = 1
  2. m=1時,無論n等於多少,都只有一種{1,1,1,,,,,,}的划分,dp[n][1] = 1
  3. n==m時,分兩種情況,dp[n][m] = 1 + dp[n][m-1]:
    1. 划分數中包含m時,只有一種{m}
    2. 划分數中不包含m時,那最大值只能是m-1,為dp[n][m-1]
  4. n>m時,m最大也只能取n,dp[n][m] = dp[n][n]
  5. n<m時,分兩種情況,dp[n][m] = dp[n-m][m] + dp[n][m-1]:
    1. 划分數中包含m時,最后一個划分數必須為m(前面也可以有m)sum{........m}=n,dp[n-m][m]
    2. 划分數中不包含m時,那最大划分數只能是m-1,dp[n][m-1]

 

遞歸比較方便,先放記憶化遞歸的代碼:

#include<iostream>
using namespace std;
const int MAXN = 128;
int dp[MAXN][MAXN] = {0};
int DP(int n, int m)
{
    // 不合法情況
    if (n <= 0 || m <= 0) return 0;

    // 記憶化
    if (dp[n][m] > 0) return dp[n][m];

    // 1. n=1只有一種{1}; 
    // 2. m=1只有一種{1,1,1,,,,,,}
    if (n == 1 || m == 1) return dp[n][m] = 1; 

    // 1. 包含m只有一種{m}
    // 2. 不包含m, dp[n][m-1]
    // n==m,這兩種情況為所有情況
    if (n == m) return dp[n][m] = 1 + DP(n, m - 1);

    // m最大也只能是n
    if (n < m)  return dp[n][m] = DP(n, n);

    // 1. 包含m, 將最后一個數定為m, dp[n-m][m]
    // 2. 不包含m, 最大的數為m-1, do[n][m-1]
    // n>m,這兩種情況為所有情況
    if (n > m)  return dp[n][m] = DP(n - m, m) + DP(n, m - 1);

    // 不合法情況
    return dp[n][m] = 0;
}
int main()
{
    int n;
    while (cin >> n) {
        cout << DP(n, n) << endl;
    }
    return 0;
}

 

DP遞推版

#include<iostream>
using namespace std;
const int MAXN = 128;
int dp[MAXN][MAXN] = { 0 };
void init(int maxn)
{
    for (int n = 1; n <= maxn; n++) {
        for (int m = 1; m <= maxn; m++) {
            
            // n=1時,無論m等於多少,都只有一種{1}的划分,dp[1][m] = 1
            // m = 1時,無論n等於多少,都只有一種{ 1,1,1,,,,,, }的划分,dp[n][1] = 1
            if (n == 1 || m == 1) dp[n][m] = 1;

            // n==m時,分兩種情況,dp[n][m] = 1 + dp[n][m-1]:
            //  1. 划分數中包含m時,只有一種{ m }
            //  2. 划分數中不包含m時,那最大值只能是m - 1,為dp[n][m - 1]
            else if (n == m) dp[n][m] = 1 + dp[n][m-1]; 

            // n>m時,m最大也只能取n,dp[n][m] = dp[n][n]
            else if (n < m)  dp[n][m] = dp[n][n];

            // n<m時,分兩種情況,dp[n][m] = dp[n-m][m] + dp[n][m-1]:
            //  1. 划分數中包含m時,最后一個划分數必須為m(前面也可以有m)sum{........m}=n,dp[n-m][m]
            //  2. 划分數中不包含m時,那最大划分數只能是m-1,dp[n][m-1]
            else if (n > m)  dp[n][m] = dp[n - m][m] + dp[n][m - 1];
        }
    }
}
int main()
{
    init(120);

    int n;
    while (cin >> n) {
        cout << dp[n][n] << endl;
    }
    return 0;
}

 

 -------------------------------------------------------------------------------------

現在考慮一些更加復雜的情況

 http://bailian.openjudge.cn/practice/4119?lang=en_US

https://vjudge.net/problem/OpenJ_Bailian-4119

第一行: N划分成K個正整數之和的划分數目
第二行: N划分成若干個不同正整數之和的划分數目
第三行: N划分成若干個奇正整數之和的划分數目

具體舉例:對於N=5,K=2

第一行: 4+1, 3+2,
第二行: 5,4+1,3+2
第三行: 5,1+1+3, 1+1+1+1+1+1

#include<iostream>
using namespace std;
const int MAXN = 55;

int dp1[MAXN][MAXN] = { 0 };
void init1(int maxn)
{
    // DP狀態設計,dp[n][k]表示數字n,被划分成k個數的組合情況數,初始化為0
    // 定下了K個數,那么以這K個數中包不包括1分為兩個情況,這樣都能做狀態轉移
    for (int n = 1; n <= maxn; n++) {
        for (int k = 1; k <= maxn; k++) {

            // 1. k=1時只有一種,{n}
            // 2. k=n時只有一種,{1,1,1,,,,,,}
            if (k == 1 || k == n) dp1[n][k] = 1;

            // 1. 這k個數字中沒有1, dp[n-k][k]為每個數字減1的情況數
            // 2. 這k個數字中有1, dp[n-1][k-1]該情況下加上數字1
            if (n > k) dp1[n][k] = dp1[n - k][k] + dp1[n - 1][k - 1];

            // n < k 為0
        }
    }
}

int init2(int maxn)
{

}

int main()
{
    init1(50);

    int n, k;
    while (cin >> n >> k) {
        cout << dp1[n][k] << endl;
    }
    return 0;
}

 

-------------------------------------------------------------------------------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM