AcWing 900. 整数划分(计数类dp or 完全背包求方案数)

24 篇文章 0 订阅
1 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述

题意:

一个正整数 n 可以表示成若干个正整数之和,形如:n = n1 + n2 + ... + ng,其中 n1>n2>...> nk,k > 1
我们将这样的一种表示称为正整数 n 的一种划分。
现在给定一个正整数 n,请你求出 n 共有多少种不同的划分方法。

解法一 计数dp

f[i,j] 的状态表示:

集合:所有总和是 i,并且恰好表示成 j 个数的和的方案

属性Count

f[i,j] 的状态计算:

我们以 “最小值是 1”、“最小值大于 1” 进行集合划分(不重不漏)

  • 当 “最小值是 1”时,由于此子集中 每一个方案都存在 1,如果将其中的 每一个方案都 “去掉一个 1,就会转化成 “和为 i-1,并且 恰好表示成 i-1 个数的和” 的方案,两者是一一映射的关系,方案数量相同。根据定义,表示为:f[i-1,j-1]

  • 当 “最小值大于 1”时,由于其中每个数都是大于 1 的,那么我们可以将 每个数都减去 1(减去之后仍然是正整数),就会转化成“和为 i-j,并且 恰好表示成 j 个数的和” 的方案,两者是一一映射的关系,方案数量相同。根据定义,表示为 f[i-j,j]

综上 可得出 状态转移方程 f [ i , j ] = f [ i − 1 , j − 1 ] + f [ i − j , j ] f[i,j] = f[i-1,j-1] + f[i-j,j] f[ij]=f[i1j1]+f[ijj]

根据定义,显然最终答案需要将 所有能组成和为 n 的方案数都累加起来 a n s = f [ n , 1 ] + f [ n , 2 ] + . . . + f [ n , n ] ans = f[n,1] + f[n,2] + ... + f[n,n] ans=f[n1]+f[n2]+...+f[nn]

#include<bits/stdc++.h>

using namespace std;
const int N = 1010, mod = 1e9+7;
int n;
int dp[N][N];

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

解法一 完全背包求方案数

思想详见:AcWing 1021. 货币系统(完全背包求取方案数量)

#include<bits/stdc++.h>

using namespace std;
const int N = 1010, mod = 1e9+7;
int dp[N];
int n;

int main()
{
    cin>>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;
        }
    }
    
    cout<<dp[n]<<endl;
    
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值