[递归,动态规划] 和为定值的子集合

2023-11-26 11:36

本文主要是介绍[递归,动态规划] 和为定值的子集合,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

和为定值的子集数

题目描述

已知 n 个正整数,wi  (1≤i≤n) 形成一个集合 W={w1,w2,...,wn},集合中的元素彼此不相同。给定某个正整数 M ,集合W中可否存在子集,该子集的所有元素之和和恰好为M,问:这样的子集有多少个?
例如,4个正整数为:
11 13 24 7
若给定M=31,则有两个子集{7,11,13}和{7,24}
于是,这样的子集有 2 个。

关于输入

第1行,输入若干个正整数,表示集合的元素,各整数之间以空格间隔;
后面有多行,每行表示一个 M 值;若某行的 M 值为0,则结束。

关于输出

对应的每个有效的 M 值,输出相应行的子集数目

例子输入
11 13 24 7
31
24
45
55
0
例子输出
2
2
0
1
解题分析

这个问题可以使用动态规划(Dynamic Programming)来解决。动态规划是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

在这个问题中,我们需要找到所有和为特定值 M 的子集的数量。我们可以使用一个二维动态规划数组 dp[i][j] 来存储到第 i 个元素为止,和为 j 的子集的数量。

这个问题的状态转移方程可以这样定义:

1. 如果当前集合的总和 M 为0,那么只有一个子集满足条件,即空子集,所以返回1。
2. 如果我们已经查看过所有的元素(i < 0),或者当前的总和 M 为负数,那么没有子集满足条件,返回0。
3. 如果当前元素 a[i] 大于当前的总和 M,那么我们不能包含这个元素,所以只能查看前 i-1 个元素,看看他们能否组成和为 M 的子集。所以 dp[i][M] = sum(i-1, M)。
4. 如果当前元素 a[i] 小于或等于当前的总和 M,那么我们有两种选择:包含这个元素或者不包含这个元素。所以 dp[i][M] = sum(i-1, M) + sum(i-1, M-a[i])。

在主函数中,我们首先使用一个循环来读取输入的元素,并将它们存储在数组 a 中。然后,我们使用另一个循环来读取输入的 M 值,并调用 sum 函数来计算满足条件的子集的数量。最后,我们将结果输出到屏幕上。

注意,为了提高效率,我们使用了记忆化搜索(也称为缓存)。我们使用一个二维数组 dp 来存储已经计算过的子问题的结果。在 sum 函数中,我们首先检查 dp[i][M] 是否已经被计算过。如果是,我们就直接返回它。否则,我们就计算它,然后将结果存储在 dp[i][M] 中,以便以后使用。

这种方法的时间复杂度是 O(n*M),其中 n 是集合的元素数量,M 是目标和。空间复杂度也是 O(n*M),用于存储 dp 数组。

代码实现
#include <iostream>
#include <cstring>
using namespace std;int M, a[10000];
int len = 0;
int dp[10000][10000]={0};int sum(int i, int M) {if (M == 0) return 1;if (i < 0 || M < 0) return 0;if(dp[i][M]!=-1) return dp[i][M];if (a[i] > M) {dp[i][M]=sum(i-1,M);return dp[i][M];} else {dp[i][M]=sum(i - 1, M) + sum(i - 1, M - a[i]);return dp[i][M];}
}int main() {char c;memset(dp,-1,sizeof(dp));while (cin >> a[len++]) {c = getchar();if (c == '\n') break;}while (cin >> M) {if (M == 0) break;cout << sum(len - 1, M) << endl;}return 0;
}

方法2:

解题分析2

这个问题可以使用动态规划来解决。我们可以构建一个二维数组dp,其中dp[i][j]表示使用前i个数字组成和为j的子集的数量。

初始条件是dp[i][0] = 1,表示使用前i个数字组成和为0的子集只有一种可能,即一个空集。然后我们可以根据以下状态转移方程来填充dp数组:

dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]], 如果 j >= nums[i - 1]
dp[i][j] = dp[i - 1][j], 如果 j < nums[i - 1]

其中nums[i - 1]表示第i个数字。第一个条件表示我们可以选择包含第i个数字或者不包含第i个数字,第二个条件表示如果当前的和j小于第i个数字,那么我们不能选择第i个数字。

这个算法的时间复杂度是O(n * M),空间复杂度也是O(n * M),其中n是集合中的数字数量,M是给定的目标和。

#include <iostream>
#include <cstring>
using namespace std;int M, a[10000];
int len = 0;
int dp[10000][10000]={0};int main() {char c;while (cin >> a[len++]) {c = getchar();if (c == '\n') break;}while (cin >> M) {if (M == 0) break;for(int i=0;i<len;i++){dp[i][0]=1;}for(int i=0;i<len;i++){for(int j=1;j<=M;j++){if(i==0){if(a[i]==j){dp[i][j]=1;}continue;}if(a[i]>j){dp[i][j]=dp[i-1][j];}else{dp[i][j]=dp[i-1][j]+dp[i-1][j-a[i]];}}}cout<<dp[len-1][M]<<endl;}return 0;
}

这篇关于[递归,动态规划] 和为定值的子集合的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/425615

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2

轨迹规划-B样条

B样条究竟是干啥的?白话就是给出一堆点,用样条的方式,给这些点连接起来,并保证丝滑的。 同时B样条分为准均匀和非均匀,以下为准均匀为例。 参考链接1:https://zhuanlan.zhihu.com/p/50626506https://zhuanlan.zhihu.com/p/50626506 参考链接2: https://zhuanlan.zhihu.com/p/536470972h

PMBOK® 第六版 规划进度管理

目录 读后感—PMBOK第六版 目录 规划进度管理主要关注为整个项目期间的进度管理提供指南和方向。以下是两个案例,展示了进度管理中的复杂性和潜在的冲突: 案例一:近期,一个长期合作的客户因政策要求,急需我们为多家医院升级一个小功能。在这个过程中出现了三个主要问题: 在双方确认接口协议后,客户私自修改接口并未通知我们,直到催进度时才发现这个问题关于UI设计的部分,后台开发人员未将其传递给

LeetCode:64. 最大正方形 动态规划 时间复杂度O(nm)

64. 最大正方形 题目链接 题目描述 给定一个由 0 和 1 组成的二维矩阵,找出只包含 1 的最大正方形,并返回其面积。 示例1: 输入: 1 0 1 0 01 0 1 1 11 1 1 1 11 0 0 1 0输出: 4 示例2: 输入: 0 1 1 0 01 1 1 1 11 1 1 1 11 1 1 1 1输出: 9 解题思路 这道题的思路是使用动态规划

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT