整数划分问题的三种解法

题目描述

题目链接
在这里插入图片描述

样例:
5=
1 1 1 1 1
2 1 1 1
2 2 1
3 1 1
3 2
4 1
5
共7种

解法一:转化为完全背包求方案数问题

背包体积为:n
物品体积为:{1,2,3…n}
求恰好装满背包需要的方案数,每种物品可以装无数次,是一个完全背包问题
![在这里插入图片描述](https://img-blog.csdnimg.cn/878b659a13244da1b44b427db21b4891.png
代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1010,mod=1e9+7;
int f[N];
int main(){
    int n;
    cin>>n;
    f[0]=1;
    for(int i=1;i<=n;i++)   
        for(int j=i;j<=n;j++)
            f[j]=(f[j]+f[j-i])%mod;
    cout<<f[n];
    return 0;
}

解法二:不同的状态转移

在这里插入图片描述

状态表示:
f[i][j]表示为总和为i,由j个数组成的方案数

状态转移:
f[i][j],考虑j个数可以分为两种情况:包含1、不包含(大于)1
包含1,表示j个元素最小值为1,此时的方案数为f[i-1][j-1](去掉这个1的方案数)
不包含(大于)1,表示j个元素最小值大于1,此时的方案数为f[i-j][j](每个元素都-1)
f[i][j]=f[i-1][j-1]+f[i-j][j]

代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1010,mod=1e9+7;
int f[N][N];

int main(){
    int n;
    cin>>n;
    f[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++) 
            f[i][j]=(f[i-1][j-1]+f[i-j][j])%mod;
    int res=0;
    for(int i=1;i<=n;i++) res=(res+f[n][i])%mod;
    cout<<res;
    return 0;
}

解法二:分治

考虑分治成子问题:
f[i][j]表示总和为i,分解出的元素中最大的数<=j的方案数
(1)显然i<j时,可分的最大数为i到达不了j,所以
f[i][j]=f[i][i]
(2)i=j
f[i][j]=f[i][j-1]+1
加上的的这一次为n本身这一次
(3)i>j时
f[i][j]=f[i][j-1]+f[i-j][j]
f[i][j-1]表示可分的数最大值为j-1的方案数,再加上最大值为j的方案即可(注意:至少有一个j,所以表示为f[i-j][j])

代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1010,mod=1e9+7;
int f[N][N];

int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i<j) f[i][j]=f[i][i];
            else if(i==j) f[i][j]=(f[i][j-1]+1)%mod;
            else f[i][j]=(f[i][j-1]+f[i-j][j])%mod;
    cout<<f[n][n];
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
整数划分是一个经典的数学问题,可以用递归的方式进行求解。具体思路如下: 1. 首先确定递归的边界条件,即当划分的整数为1时,只有一种划分方式。 2. 对于大于1的整数,可以分成两种情况:一种是包含1的划分方式,一种是不包含1的划分方式。 3. 对于包含1的划分方式,可以递归求解剩余的部分。 4. 对于不包含1的划分方式,可以递归求解剩余部分,但是需要限制划分的整数不能大于原来的整数。 下面是一个简单的整数划分的C语言代码实现: ```c #include <stdio.h> int integerPartition(int n, int m) { if (n == 1 || m == 1) { return 1; } else if (n < m) { return integerPartition(n, n); } else if (n == m) { return 1 + integerPartition(n, m - 1); } else { return integerPartition(n, m - 1) + integerPartition(n - m, m); } } int main() { int n; printf("请输入一个正整数n:"); scanf("%d", &n); printf("整数划分的个数为:%d\n", integerPartition(n, n)); return 0; } ``` 在上面的代码中,`integerPartition`函数接受两个参数n和m,表示将n划分成不大于m的正整数的划分方式数。函数根据递归边界条件和上述思路进行递归求解,并返回划分方式数。主函数中从输入中读取n,调用`integerPartition`函数计算划分方式数,并输出结果。 需要注意的是,整数划分问题解法存在重复计算的情况,可以采用动态规划的方式优化求解效率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chp的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值