引言

既上一篇 子集系列(一) 后,这里我们接着讨论带有附加条件的子集求解方法。

这类题目也是求子集,只不过不是返回所有的自己,而往往是要求返回满足一定要求的子集。

解这种类型的题目,其思路可以在上一篇文章的思路略作改进。

例 1,求元素数量为定值的所有子集

Combinations

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

For example,
If n = 4 and k = 2, a solution is:

[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
class Solution {
public:
vector<vector<int> > combine(int n, int k) {
}
}

思路1:这道题本质上就是求 [1,2,...n] 这个集合中,个数为k的所有子集。

利用上篇文章的思路:每一个元素,在子集中都有两种可能:出现 OR 不出现。我们只要将两种情况都判断一下,看当前子集是否满足要求就好,在本题中,需要满足的要求是:size == k

class Solution {
public:
vector<vector<int> > combine(int n, int k) {
if(n < k) return res;
combineCore(, n, k);
return res;
}
private:
vector<int> path;
vector<vector<int> > res;
void combineCore(int st, int n, int k){
if(path.size() == k){
res.push_back(path);
return;
}
if(st > n) return;
combineCore(st+, n, k); //case1: skip
path.push_back(st);
combineCore(st+, n, k); //case2: not skip
path.pop_back();
}
};

AC 52ms。

思路2:当然也可以用回溯思想来做:选择子集的第一个数时,可以在 [1,2,...,n-(k-1)] 这么多数中选择,选好了第一个数后,假定选的是q,那么子集的第二个数就只能从 [q+1, q+2, .... , n-(k-2)]这些数中选了。

因此递归函数中,每次递进一次递归,k就减1,表示子集中待确定的数字越来越少,同时,要有一个参数来表示可选范围的起始元素,假设为st。

代码:

class Solution {
public:
vector<vector<int> > combine(int n, int k) {
if(n < k) return res;
vector<int> v;
combineCore(, n, k, v);
return res;
}
private:
vector<vector<int> > res;
void combineCore(int st, int n, int k, vector<int> &v){
if(k == ){
res.push_back(v);
return;
}
for(int i = st; i <= n-k+; ++i){
v.push_back(i);
combineCore(i+, n, k-, v);
v.pop_back();
}
}
};

AC 48ms。

例 2.1,求元素和为定值的所有子集

Combination Sum

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.

For example, given candidate set 2,3,6,7 and target 7
A solution set is: 
[7] 
[2, 2, 3]

class Solution {
public:
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
}
};

思路:依然从每一个元素有 出现 OR 不出现入手,注意题意:一个元素可以不被用或者使用多次。

我们可以先将candidates排序,然后去重。在这样的candidates基础上,我们考虑完“出现”的情况后,st不需要后移一位。

class Solution {
public:
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
if(target <= ) return res;
if(candidates.size() == ) return res;
sort(candidates.begin(), candidates.end()); //排序
if(candidates.size() > ){ //去重
int p = , q = ;
while(q < candidates.size()){
if(candidates[p] != candidates[q]){
candidates[++p] = candidates[q++];
}else{
++q;
}
}
candidates.erase(candidates.begin()+p+, candidates.end());
}
combinSumCore(candidates, , target);
return res;
}
private:
vector<int> path;
vector<vector<int> > res;
void combinSumCore(vector<int> &candidates, int st, int target) {
if(target == ){
res.push_back(path);
return;
}
if(target < || st >= candidates.size() || candidates[st] > target) return;
combinSumCore(candidates, st+, target); //case1: skip
path.push_back(candidates[st]);
combinSumCore(candidates, st, target - candidates[st]); //case2: not skip,但是st这里不+1,因为数可以被用多次。
path.pop_back();
}
};

AC 60ms

例 2.2,求元素和为定值的所有子集

和例2.1同样的题目,不同的是一个元素只能用一次。

Combination Sum II

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.

For example, given candidate set 10,1,2,7,6,1,5 and target 8
A solution set is: 
[1, 7] 
[1, 2, 5] 
[2, 6] 
[1, 1, 6]

class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {}
};

思路 1:题目中num中包含重复元素,每个元素只能用一次。

我们依然先将num排序,那么重复的元素肯定在一起了。对于这些重复的元素,单独提出来考虑,因为对于这种元素,其 “出现 OR 不出现”的问题不单单和整体条件有关(这里的整体条件是和为target),而且和其相邻元素有关。

以[1,2,2,2,3]为例,我们单独将其中的[2,2,2] 部分提出来考虑,这部分的组合只能是: [], [2], [2,2], [2,2,2]。

class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
if(num.size() == ) return res;
sort(num.begin(), num.end());
combinationSumCore(num, , target);
return res;
}
private:
vector<int> path;
vector<vector<int> > res;
void combinationSumCore(vector<int> &num, int start, int target) {
if(target < ) return;
if(target == ){
vector<int> v;
res.push_back(path);
return;
}
if(start < num.size()){
int i = start+;
for(; i < num.size() && num[start] == num[i]; ++i);
combinationSumCore(num, i, target);//case1: Jump 掉相同的 int sum = , j = i-;
for(; j >= start; --j){
sum += num[j];
path.push_back(num[j]);
combinationSumCore(num, i, target - sum);//case2: 这段相同段上的所有使用情况。
}
for(j = i-; j >= start; --j){
path.pop_back();
}
}
}
};

AC 96ms

思路2,不用内循环for

上一个思路中包含了一个子循环for,num的一小段上做预处理。能不能不用子循环,依然用最典型的求子集解法呢?当然可以,下面的代码就是基于subsetII 的思路二(见上篇博文) 改写而来。

class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
if(target <= ) return res;
sort(num.begin(), num.end());
combinationSumCore(, num, target);
return res;
} private:
vector<int> path;
vector<vector<int> > res;
void combinationSumCore(int st, vector<int> &num, int target)
{
if(target < ) return;
if(st == num.size()){
if(target == )
res.push_back(path);
return;
} //Handle target >= 0,下面的部分和Subset II思路二的代码一样。
if(path.size() == || path[path.size()-] != num[st])
combinationSumCore(st+, num, target);
path.push_back(num[st]);
combinationSumCore(st+, num, target-num[st]);
path.pop_back();
}
};

AC 102ms

这种思路和上面第一种思路的区别在于:target == 0并不会导致函数立即push_back(path) 并 return,只有同时也满足 st == num.size(),才会push_back(path) 并 return。

结语

有不少问题其实都可以转化为求子集的情况,只不过子集需要满足一定的条件。

对于这种问题,通过递归实现 每一个元素的“出现OR不出现” 两种情况,可以作为一种思路。

子集系列(二) 满足特定要求的子集,例 [LeetCode] Combination, Combination Sum I, II的更多相关文章

  1. scrapy爬虫学习系列二:scrapy简单爬虫样例学习

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  2. 二叉树系列 - 二叉树里的最长路径 例 [LeetCode] Binary Tree Maximum Path Sum

    题目: Binary Tree Maximum Path Sum Given a binary tree, find the maximum path sum. The path may start ...

  3. 子集系列(一) 传统subset 问题,例 [LeetCode] Subset, Subset II, Bloomberg 的一道面试题

    引言 Coding 问题中有时会出现这样的问题:给定一个集合,求出这个集合所有的子集(所谓子集,就是包含原集合中的一部分元素的集合). 或者求出满足一定要求的子集,比如子集中元素总和为定值,子集元素个 ...

  4. Subsets 子集系列问题 leetcode

    子集系列问题: Coding 问题中有时会出现这样的问题:给定一个集合,求出这个集合所有的子集(所谓子集,就是包含原集合中的一部分元素的集合). 或者求出满足一定要求的子集,比如子集中元素总和为定值, ...

  5. 【转载】PyTorch系列 (二):pytorch数据读取

    原文:https://likewind.top/2019/02/01/Pytorch-dataprocess/ Pytorch系列: PyTorch系列(一) - PyTorch使用总览 PyTorc ...

  6. Istio的流量管理(概念)(istio 系列二)

    Istio的流量管理(概念) 目录 Istio的流量管理(概念) 概述 Virtual services 为什么使用virtual service Virtual services举例 hosts字段 ...

  7. 图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)

    项目链接:https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1 欢迎fork欢迎三连!文章篇幅有限, ...

  8. [知识库分享系列] 二、.NET(ASP.NET)

    最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...

  9. ANDROID Porting系列二、配置一个新产品

    ANDROID Porting系列二.配置一个新产品 详细说明 下面的步骤描述了如何配置新的移动设备和产品的makefile运行android. 1.         目录//vendor/创建一个公 ...

随机推荐

  1. 带你玩转JavaScript中的隐式强制类型转换

    正题开始前我想先抛出一个问题,==和===有什么区别?可能一般人会想,不就是后者除了比较值相等之外还会比较类型是否相等嘛,有什么好问的,谁不知道?!但是这样说还不够准确,两者的真正区别其实是==在比较 ...

  2. HDU 5285 wyh2000 and pupil 判二分图+贪心

    题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5285 bc:http://bestcoder.hdu.edu.cn/contests/con ...

  3. jdbc 3.0

    1.将Blob.Clob类型数据保存到数据库 import java.io.File; import java.io.FileInputStream; import java.io.FileReade ...

  4. B-2阶段组员分数分配

    组名: 新蜂 组长: 武志远 组员: 宫成荣 谢孝淼 杨柳 李峤 项目名称: java俄罗斯方块 武 武 武 武 杨 宫 宫 杨 宫 谢 李 杨 李 谢 李 谢 李 谢 杨 宫 扬 谢 宫 李 武 评 ...

  5. 【Biocode】产生三行的seq+01序列

    代码说明: sequence.txt与site.txt整合 如下图: sequence.txt: site.txt: 整理之后如下: 蛋白质序列中发生翻译后修饰的位置标记为“1”,其他的位置标记为“0 ...

  6. C# 开发人员的函数式编程

    摘要:作为一名 C# 开发人员,您可能已经在编写一些函数式代码而没有意识到这一点.本文将介绍一些您已经在C#中使用的函数方法,以及 C# 7 中对函数式编程的一些改进. 尽管 .NET 框架的函数式编 ...

  7. C 语言疑难杂症 [转:http://blog.chinaunix.net/uid-20688544-id-1894880.html]

    无聊在网上找了些C语言的东东练一下手,竟然发现其实还有好多细节之前,没注意到,该好好复习一下先. 解决掉的问题先不发出来,把疑问的先做个笔记,过几天解决了就回来修改补上.   #include < ...

  8. .netMVC Vue axios 获取数据

    网页 <link href="~/Content/css/bootstrap-theme.min.css" rel="stylesheet" /> ...

  9. bzoj2253纸箱堆叠(动态规划+cdq分治套树状数组)

    Description P 工厂是一个生产纸箱的工厂.纸箱生产线在人工输入三个参数 n p a , 之后,即可自动化生产三边边长为 (a mod P,a^2 mod p,a^3 mod P) (a^4 ...

  10. 【数据库_Postgresql】sql语句添加序号,timestamp格式时间截取日期和时间

    SELECT ROW_NUMBER() OVER (ORDER BY sr.receiptid ASC) AS 序号, sr.receiptid, sr.receiptdate, DATE(sr.re ...