整数划分问题

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;
}
posted @ 2023-01-05 14:49  junlin623  阅读(371)  评论(0编辑  收藏  举报