2024/3/9d打卡整数划分---背包动态规划方式,计数类动态规划

目录

题目

DP分析

第一种方法,背包DP

 代码

第二种方法(有点难想到)

 代码


题目

一个正整数 n 可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中 n1≥n2≥…≥nk,k≥1。

我们将这样的一种表示称为正整数 n 的一种划分。

现在给定一个正整数 n,请你求出 n 共有多少种不同的划分方法。

输入格式

共一行,包含一个整数 n。

输出格式

共一行,包含一个整数,表示总划分数量。

由于答案可能很大,输出结果请对 10^9+7 取模。

数据范围

1≤n≤1000

输入样例:

5

输出样例:

7

DP分析

第一种方法,背包DP

        我们可以将 n 划分为  n_1,n_2,n_3\ ...\ n_k 可以理解为 由 n_1,n_2,n_3\ ...\ n_k 这些物品组合成 n,并且物品的数目不限。因此可以转化为完全背包问题。

状态表示:使用 f[i,j]

  • 集合:f[i,j] 表示从 [1,i] 种数字中选择,总和是 j 的方案。
  • 属性:方案的总和

状态计算:

  • 数字 i 选择 0 个,说明从前 i-1 中选,且加起来恰好是 j :f[i][j]=f[i-1][j]
  • 数字 i 选择 1 个,说明从前 i-1 中选,且加起来恰好是 j-i :f[i][j]=f[i-1][j-i]
  • 数字 i 选择 2 个,说明从前 i-1 中选,且加起来恰好是 j-2i :f[i][j]=f[i-1][j-2i]
  • ...
  • 数字 i 选择 k 个,说明从前 i-1 中选,且加起来恰好是 j-ki :f[i][j]=f[i-1][j-ki] 

     那么, f[i][j]=f[i-1][j]+f[i-1][j-i]+...+f[i-1][j-ki]

优化

       f[i][j]=f[i-1][j]+f[i-1][j-i]+...+f[i-1][j-ki]                ①

f[i][j-i]=f[i-1][j-i]+f[i-1][j-2i]...+f[i-1][j-ki]            ②

由①和②可得 f[i][j] = f[i-1][j]+f[i][j-i]

 代码

(从最后状态转移方差可以看出,方程与 i 无关,因此可以优化成一维)

import java.io.*;
import java.util.*;

class Main{
    static int N = 1010;
    static int[] f = new int[N];
    public static void main(String[] args) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(in.readLine());
        f[0] = 1; // 初始化为1是因为,在后续 j 的更新中,本身j+0也是一种方案,每次从这+1
        
        // DP
        for(int i=1;i<=n;i++){
            for(int j=i;j<=n;j++){
                f[j] = (f[j]+f[j-i])%(1000000007); 
            }
        }

        System.out.println(f[n]);
    }
}

 

第二种方法(有点难想到)

状态表示:使用 f[i,j]

  • 集合:f[i,j] 表示 i 划分为 j 个数字的所有方案。
  • 属性:方案的总和。

状态计算:

  • 划分的所有数最小值为1 :f[i,j]=f[i-1,j-1]
  • 划分中所有数全都大于1 :f[i,j]=f[i-j,j] (将所有划分的数字都-1,由于全都大于1,因此-1也是大于0的,所以将i减去j个1,数字的个数仍然不变)

因此:f[i,j]=f[i-1,j-1]+f[i-j,j]

那么i可以划分的总和为从 [1,i]的总和: f[i,1]+f[i,2]+...\ +f[i,i]

 

 代码

import java.io.*;
import java.util.*;

class Main{
    static int N = 1010;
    static int[][] f = new int[N][N];
    public static void main(String[] args) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(in.readLine());
        f[0][0] = 1; // 初始化为1是因为,在后续 i 的更新中,本身i+0也是一种方案,每次从这+1
        
        // DP
        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])%(1000000007); 
            }
        }
        int res = 0;
        for(int i=1;i<=n;i++) res = (res+f[n][i])%(1000000007);
        System.out.println(res);
    }
}

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值