今天给大家带来一篇整数拆分问题的讲解,希望大家能喜欢。
整数拆分问题是一类非常经典的动态规划问题,在2020NOI ONLINE中PJ T2出现,当时可谓难倒一片dalao,今天就以这道题来解读整数拆分问题。
题面:
题目描述
小 H 是一个热爱运动的孩子,某天他想给自己制定一个跑步计划。小 H 计划跑
米,其中第
分钟要跑
米(
是正整数),但没有确定好总时长。
由于随着跑步时间增加,小 H 会越来越累,所以小 H 的计划必须满足对于任意
都满足
。
现在小 H 想知道一共有多少个不同的满足条件的计划,请你帮助他。两个计划不同当且仅当跑步的总时长不同,或者存在一个 iii,使得两个计划中
不相同。
由于最后的答案可能很大,你只需要求出答案对
取模的结果。
输入格式
输入只有一行两个整数,代表总米数 和模数 。
输出格式
输出一行一个整数,代表答案对 取模的结果。
输入样例
4 44
输出样例
5
题面解读:
这道题就是整数拆分问题的一个很典型的应用(话说这就是裸的整数拆分 ),附上真正整数拆分问题的题面:
将一个正整数 拆分成一系列正整数之和,即: ,其中 且 。这种表示方法叫做 的拆分,求 的不同拆分的个数。
题目分析
这道题可以用完全背包的方法去做,分析一下,可以把
的大小当成体积,把拆分的数量当成完全背包的方案数,把每一个
当成物品的体积。
因为完全背包的状态转移方程为:
所以求完全背包的方案数的状态转移方程为:
将其类推到整数拆分问题上,可得:
上代码:
#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;
}
整数拆分问题空间复杂度优化
我们知道完全背包能优化空间复杂度,那么整数拆分问题可以吗?
答案是肯定的。同理,完全背包求方案数的优化后转移方程是:
以此类推,整数拆分问题的转移方程是:
上代码:
#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;
}