计数类DP

计数类DP——整数划分

整数划分大体上可以分为3类

  1. 考虑顺序的拆分方案(即1,1,2;和2,1,1 是两种不同的方案)
    • 这种问题一般转化为完全背包即可解决。
  2. 不考虑顺序的拆分方案,可以划分出空集(也就是可以有对拆分完全没贡献的东西存在(0))
  3. 不考虑顺序的拆分方案,要求划分出的集合不为空集(不可以拆出0)

主要讨论2,3类DP问题

空集存在的情况

对于n的m划分,可以定义dp[m][n] 为所求答案, 考虑任意的拆分序列 a i {a_i} ai,可以分为两类
1. 所有 a i a_i ai都大于0,我们可以每个序列都拿出一个1 ,则拆分序列变为 a i − 1 {a_i - 1} ai1,它的求和为 n − m n-m nm,所以他是 n − m 的 m 划 分 n-m的m划分 nmm,对答案的贡献为dp[i][j-i].
2. 如果存在 a i = 0 a_i = 0 ai=0,可以将之化归到 n 的 m − 1 划 分 n的m-1划分 nm1,对答案的贡献为dp[i-1][j]

从而,有: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j] = dp[i-1][j] + dp[i][j-1] dp[i][j]=dp[i1][j]+dp[i][j1]
当划分的数量m太大( m > n m>n m>n)后,第一种情况不存在,
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]

代码

#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
#define lowbit(x) x&-x
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 200, mod = 1e9 + 7;
int n,m;
ll dp[N][N];// j 的 i划分
void solve()
{    
    cin >> n >> m;
    // dp[i][j] = dp[i-1][j] + dp[i][j - i] 
    dp[0][0] = 1;
    for(int i=1;i<=m;i++){
        for(int j=0;j<=n;j++){
            if(j - i >= 0){
                dp[i][j] = dp[i][j-i] + dp[i-1][j];
            }else{
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    cout << dp[m][n] << endl;
}
signed main()
{
    ios::sync_with_stdio();cin.tie();cout.tie();

    solve();

    return 0;
}

空集不存在的情况

在原来的基础上只要解决了重复计数的部分即可
显然就是不在考虑 a i {a_i} ai中存在0的情况,把上面代码else去掉.
并且注意初始化dp时,初始化为dp[m][0] = 1 ,防止重复计算

找到一道板子题

代码

#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
#define lowbit(x) x&-x
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 200, mod = 1e9 + 7;
int n,m;
ll dp[N][N];// j 的 i划分
void solve()
{    
    cin >> n >> m;
    // dp[i][j] = dp[i-1][j] + dp[i][j - i] 
    dp[0][m] = 1;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(j - i >= 0){
                dp[i][j] = dp[i][j-i] + dp[i-1][j];
            }
        }
    }
    cout << dp[m][n] << endl;
}
signed main()
{
    ios::sync_with_stdio();cin.tie();cout.tie();

    solve();

    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值