整数拆分问题解析

今天给大家带来一篇整数拆分问题的讲解,希望大家能喜欢。


整数拆分问题是一类非常经典的动态规划问题,在2020NOI ONLINE中PJ T2出现,当时可谓难倒一片dalao,今天就以这道题来解读整数拆分问题。

题面:

题目描述

小 H 是一个热爱运动的孩子,某天他想给自己制定一个跑步计划。小 H 计划跑 n n n 米,其中第 i ( i ≥ 1 ) i(i≥1) i(i1) 分钟要跑 x i x_i xi​ 米( x i x_i xi 是正整数),但没有确定好总时长。
由于随着跑步时间增加,小 H 会越来越累,所以小 H 的计划必须满足对于任意 i ( i > 1 ) i(i>1) i(i>1) 都满足 x i ≤ x i − 1 x_i≤x_i−1 xixi1
现在小 H 想知道一共有多少个不同的满足条件的计划,请你帮助他。两个计划不同当且仅当跑步的总时长不同,或者存在一个 iii,使得两个计划中 x i x_i xi不相同。
由于最后的答案可能很大,你只需要求出答案对 p p p 取模的结果。

输入格式

输入只有一行两个整数,代表总米数 n n n 和模数 p p p

输出格式

输出一行一个整数,代表答案对 p p p 取模的结果。

输入样例
4 44
输出样例
5

题面解读:

这道题就是整数拆分问题的一个很典型的应用(话说这就是裸的整数拆分 ),附上真正整数拆分问题的题面:

将一个正整数 n n n 拆分成一系列正整数之和,即: n = n 1 + n 2 + n 3 + … … + n k n = n_1 + n_2 + n_3 + …… + n_k n=n1+n2+n3++nk,其中 n 1 ≥ n 2 ≥ n 3 ≥ … … ≥ n k ≥ 1 n_1 \ge n_2 \ge n_3 \ge …… \ge n_k \ge 1 n1n2n3nk1 k ≥ 1 k \geq 1 k1。这种表示方法叫做 n n n 的拆分,求 m m m 的不同拆分的个数。

题目分析

这道题可以用完全背包的方法去做,分析一下,可以把 m m m 的大小当成体积,把拆分的数量当成完全背包的方案数,把每一个 n i n_i ni 当成物品的体积。
因为完全背包的状态转移方程为: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − c [ i ] ] + w [ i ] ) dp[i][j] = max(dp[i - 1][j] , dp[i][j - c[i]] + w[i]) dp[i][j]=max(dp[i1][j],dp[i][jc[i]]+w[i])
所以求完全背包的方案数的状态转移方程为: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − c [ i ] ] dp[i][j] = dp[i - 1][j] + dp[i][j - c[i]] dp[i][j]=dp[i1][j]+dp[i][jc[i]]
将其类推到整数拆分问题上,可得: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − i ] dp[i][j] = dp[i - 1][j] + dp[i][j - i] dp[i][j]=dp[i1][j]+dp[i][ji]

上代码:

#include 
#include 
#include 
using namespace std;
const int N = 1e3 + 9;
const int mod = 1e9 + 9;  //取模
int f[N];
int main() {
    //freopen("divide.in", "r", stdin);
    //freopen("divide.out", "w", stdout);
    memset(f, 0, sizeof f);
    f[0][0] = 1;
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= n; ++j) {
            if(j >= i) {
    		f[i][j] = f[i][j - i] + f[i - 1][j];  //状态转移
   	    } 
            else {
                f[i][j] = f[i - 1][j];   //边界条件
            }
        }
    }
    cout << f[n][n] << endl;
    return 0;
}

整数拆分问题空间复杂度优化

我们知道完全背包能优化空间复杂度,那么整数拆分问题可以吗?

答案是肯定的。同理,完全背包求方案数的优化后转移方程是: d p [ j ] = d p [ j − c [ i ] ] + d p [ j ] dp[j] = dp[j - c[i]] + dp[j] dp[j]=dp[jc[i]]+dp[j]
以此类推,整数拆分问题的转移方程是: d p [ j ] = d p [ j ] + d p [ j − i ] dp[j] = dp[j] + dp[j - i] dp[j]=dp[j]+dp[ji]

上代码:

#include 
#include 
#include 
using namespace std;
const int N = 1e3 + 9;
const int mod = 1e9 + 9;
int f[N];
int main() {
    //freopen("divide.in", "r", stdin);
    //freopen("divide.out", "w", stdout);
    int t;
    cin >> t;
    f[0] = 1;
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = i; j <= n; ++j) {
           (f[j] += f[j - i]) %= mod;
        }
    }
    cout << f[n] << endl;
    return 0;
}

END

你可能感兴趣的:(上课笔记,动态规划专题讲解)