AcWing刷题——数字组合 (0-1背包)
题目描述:
给定 N 个正整数 A1,A2,…,AN,从中选出若干个数,使它们的和为 M,求有多少种选择方案。
输入格式
第一行包含两个整数 N和 M。
第二行包含 N个整数,表示 A1,A2,…,AN。
输出格式
包含一个整数,表示可选方案数。
数据范围
1≤N≤100
1≤M≤10000,
1≤Ai≤1000
输入样例:
4 4
1 1 2 2
输出样例:
3
思路:
本题是求有多少种方式,一眼看过去很容易想到是用动态规划来解题,而且是序列型动态规划。
分析:
先看最后一步,题目让我们求从给定的N个数中选出若干个数来,使其之和为M的方式有多少种,此时我们可以关注最后一个数,如果取最后一个数,那么前面必定取的数中之和为M - a[i],那么问题来了。现在我们需要求的就是前N - 1个数中取出若干个数的和为M - a[i]的方式有多少种,可以看的出来我们已经把原问题分解为结构相似的子问题了,根据这种策略一直分解下去,那么最终我们就可以得到一个最小子问题且可以对这个最小子问题进行直接求解,最后再通过这些子问题的解来得到原问题的解即可。
状态:dp[i][j] 表示前i个数字中选出的若干数之和为j的方案有多少种
转移方程:dp[i][j] = dp[i - 1][j] + dp[i - 1][j - a[i]] ,需要满足一定的条件,才可以用这个式子,下面的代码中有。
AC代码:
1 import java.util.*; 2 public class Main{ 3 public static void main(String[] args) { 4 Scanner input = new Scanner(System.in); 5 int N = input.nextInt(); 6 int M = input.nextInt(); 7 int[] arr = new int[N]; 8 for (int i = 0; i < N; i++) { 9 arr[i] = input.nextInt(); 10 } 11 12 13 // 时间复杂度:O(N ^ 2) 14 // 空间复杂度:O(MN) 15 // dp[i][j] 表示前i个数字中选出的若干数之和为j的方案有多少种 序列型动态规划,因为这里我们需要知道前i个数的和,所以就需要开个二维数组 16 int[][] dp = new int[N + 1][M + 1]; 17 18 // 初始化 19 for (int i = 0; i <= M; i++) { 20 dp[0][i] = 0; 21 } 22 23 for (int i = 0; i <= N; i++) { 24 dp[i][0] = 0; 25 } 26 27 28 for (int i = 1; i <= N; i++) { 29 for (int j = 1; j <= M; j++) { 30 if (arr[i - 1] == j) { 31 dp[i][j] = dp[i - 1][j] + 1; 32 continue; 33 } 34 if (arr[i - 1] > j) { 35 dp[i][j] = dp[i - 1][j]; 36 continue; 37 } 38 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - arr[i - 1]]; 39 } 40 } 41 42 System.out.println(dp[N][M]); 43 } 44 45 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能