整数拆分问题解析

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


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

题面:

题目描述

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

输入格式

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

输出格式

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

输入样例
4 44
输出样例
5

题面解读:

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

将一个正整数 nn 拆分成一系列正整数之和,即:n=n1+n2+n3++nkn = n_1 + n_2 + n_3 + …… + n_k,其中 n1n2n3nk1n_1 \ge n_2 \ge n_3 \ge …… \ge n_k \ge 1k1k \geq 1。这种表示方法叫做 nn 的拆分,求 mm 的不同拆分的个数。

题目分析

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

上代码:

#include <iostream>
#include <cstring>
#include <cstdio>
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;
}

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

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

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

上代码:

#include <iostream>
#include <cstring>
#include <cstdio>
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

posted @ 2020-03-23 22:54  进击的蒟蒻  阅读(554)  评论(0)    收藏  举报