整数划分问题
1. 不允许划分的整数之间重复
一个正整数 n 可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中 n1>n2>…>nk,k≥1。
我们将这样的一种表示称为正整数 n 的一种划分。
现在给定一个正整数 n,请你求出 n 共有多少种不同的划分方法。
该类问题实际上都可以转换为背包问题模型
将题意转换一下:1~n中选择若干个数使得这些数的和为n,问方案数有多少
那么1~n中的,每个数都看作是一个物品的重量,每个物品都有选与不选两种,将这些物品放到容量为n的背包中并且要求背包恰好被装满的方案数
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, mod = 1e9+7;
int dp[N], n;
int main() {
scanf("%d", &n);
dp[0] = 1;
for(int i = 1;i <= n;i++) {
for(int j = n;j >= i;j--)
dp[j] = (dp[j] + dp[j-i]) % mod;
}
printf("%d\n", dp[n]);
return 0;
}
样例:
输入:5
5=1+4
5=2+3
5=5
输出:3
2. 允许划分的整数之间重复
与上一题的题干相同,不过是要求 n1≥n2≥…≥nk,k≥1。
允许每个数选择多个,如3=1+1+1这也算一种方案
所以这种问题就转换为完全背包模型
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, mod = 1e9+7;
int dp[N], n;
int main() {
scanf("%d", &n);
dp[0] = 1;
for(int i = 1;i <= n;i++) {
for(int j = i;j <= n;j++)
dp[j] = (dp[j] + dp[j-i]) % mod;
}
printf("%d\n", dp[n]);
return 0;
}
样例:
输入:5
5=1+1+1+1+1
5=1+1+1+2
5=1+1+3
5=1+2+2
5=1+4
5=2+3
5=5
输出:7
3. 限定划分的数的个数:
比如十三届蓝桥杯国赛C\C++ B组的第一题:
将2022拆分成10个互不相同的正整数之和,总共有多少种方法?
注意:交换顺序视为同一种方法,例如2022 = 1000 + 1022 和 2022 = 1022 + 1000就视为同一种方法
① 要求正整数之间互不相同,所以是01背包模型
② 限定选择的正整数的个数为10个,所以这是个二维背包模型,也就是除了重量之外多了一个限定条件
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[11][2222];
int main() {
dp[0][0] = 1;
for(int i = 1;i <= 2022;i++) { //枚举物品
for(int j = 10;j >= 1;j--) { //枚举个数
for(int k = 2022;k >= i;k--){ //枚举容量
dp[j][k] += dp[j-1][k-i];
}
}
}
printf("%lld\n", dp[10][2022]); //379187662194355221
return 0;
}
你只管出发,旅途自有风景~~