▶ 给定一个数组 和一个目标值。从该数组中选出若干项(项数不定),使他们的和等于目标值。

▶ 36. 数组元素无重复

● 代码,初版,19 ms 。从底向上的动态规划,但是转移方程比较智障(将待求数分解为左右两个半段,分别找解,拼在一起,再在接缝上检查是否是重复解)。

 class Solution
{
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target)
{
sort(candidates.begin(), candidates.end());
int n, i, j, k;
vector<int> temp;
vector<int>& cand = candidates;
vector<vector<int>> left, right;
vector<vector<vector<int>>> table(target - cand[] + );
unordered_map<int, int> map; if (target < cand[])
return vector<vector<int>>{};
for (i = ; i < candidates.size(); i++)
map.emplace(candidates[i], i);
for (n = cand[]; n <= target; n++) // 每次循环计算一个数 n
{
if (map.find(n) != map.end()) // 原数组中有 n,本身就是一个解
table[n - cand[]].push_back(vector<int>{n});
if (!(n % ) && map.find(n / ) != map.end()) // 将偶数 n 分拆为两个 n/2 的和的解
table[n - cand[]].push_back(vector<int>{n/, n/});
for (i = cand[]; i <= (n - ) / ; i++) // 将 n 分解为左右两个半段,将各自的解进行笛卡尔积
{
left = table[i - cand[]];
right = table[n - i - cand[]];
if (left.size() == || right.size() == ) // 左右半段至少一个为空,不可照此分解
continue;
for (j = ; j < left.size(); j++)
{
for (k = ; k < right.size(); k++)
{
if (left[j][left[j].size() - ] > right[k][] ||
left[j].size() > && table[i - left[j][left[j].size() - ] - cand[]].size() > && table[n - i + left[j][left[j].size() - ] - cand[]].size() > )
continue;// 删除解的条件:左半段最后一个元素比右半段第一个元素大(不保持单调);将左半段最后一个元素转移给右半段后仍是一个解(该解一定在之前的搜索中出现过)
temp = left[j];
temp.insert(temp.end(), right[k].begin(), right[k].end());
table[n - cand[]].push_back(temp);
}
}
}
}
return table[target - cand[]];
}
};

● 代码,成套方法系列,13 ms 。从顶向下的递归,向解向量集中逐一添加元素。最快的解法算法与之相同。

 class Solution
{
public:
vector<vector<int> > combinationSum(vector<int> &candidates, int target)
{
sort(candidates.begin(), candidates.end());
vector<vector<int>> res;
vector<int> combination;
sum(candidates, target, res, combination, );
return res;
}
void sum(vector<int> &candidates, int target, vector<vector<int>> &res, vector<int> &combination, int begin)
{
if (!target)
{
res.push_back(combination);
return;
}
for (int i = begin; i < candidates.size() && target >= candidates[i]; i++)// 每次向现有组合中添加一项,使用剩下的项来求解一个更小的和
{
combination.push_back(candidates[i]);
sum(candidates, target - candidates[i], res, combination, i);
combination.pop_back();
}
}
};

● 代码,大佬的版本,9 ms 。与上面的成套方法基本相同。

 class Solution
{
public:
vector<vector<int>> sum(vector<int>& candidates, int m, int target)
{
vector<vector<int>> output, x;
int c, rep;
for (int i = m; i < candidates.size(); i++)
{
if (candidates[i] > target)
break;
for (c = ; target - c * candidates[i] >= ; c++)
{
if (target - c * candidates[i] == )
{
output.push_back(vector<int>(c, candidates[i]));
break;
}
x = sum(candidates, i + , target - c * candidates[i]);
for (auto & k : x)
{
for(rep = c;rep > ;rep--)
k.push_back(candidates[i]);
output.push_back(k);
}
}
}
return output;
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target)
{
sort(candidates.begin(), candidates.end());
return sum(candidates, , target);
}
};

▶ 39.数组元素有重复(给重复解检查造成困难)

● 代码,成套方法系列,12 ms 。添加一个跳过相同元素的判断。

 class Solution
{
public:
vector<vector<int> > combinationSum2(vector<int> &candidates, int target)
{
sort(candidates.begin(), candidates.end());
vector<vector<int>> res;
vector<int> combination;
sum(candidates, target, res, combination, );
return res;
}
void sum(vector<int> &candidates, int target, vector<vector<int>> &res, vector<int> &combination, int begin)
{
if (!target)
{
res.push_back(combination);
return;
}
for (int i = begin; i < candidates.size() && target >= candidates[i]; i++)// 每次向现有组合中添加一项,使用剩下的项来求解一个更小的和
{ if (i != begin && candidates[i] == candidates[i - ])// 跳过相同的元素,否则会出现重复解
continue;
combination.push_back(candidates[i]);
sum(candidates, target - candidates[i], res, combination, i + );
combination.pop_back();
}
}
};

● 大佬的代码,12 ms,算法与上面相同。

 class Solution
{
public:
vector<vector<int>> sum(vector<int>& candidates, int m, int target)
{
vector<vector<int>> output, x;
int i, j, k, c;
for (i = m; i < candidates.size();i++)
{
if (candidates[i] > target)// 最小元素过大
break;
for (j = i; i < candidates.size() - && candidates[i] == candidates[i + ]; i++);// 跳过重复元素
for (k = , c = i - j + ; k <= c; k++) // 尽量多的选取 candidates[j]
{
if (k * candidates[j] == target)
{
output.push_back(vector<int>(k, candidates[j]));
break;
}
else if (k * candidates[j] > target)
break;
vector<int> first(k, candidates[j]); // 选定 k 个 candidates[j]
x = sum(candidates, i + , target - k*candidates[j]); // 剩下的工作由递归完成
for (auto& k : x)
{
vector<int> second = first;
for (auto p : k) second.push_back(p);
output.push_back(second);
}
}
}
return output;
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target)
{
sort(candidates.begin(), candidates.end());
return sum(candidates, , target);
}
};

▶ 216. 没有给出确定的数组,而是指定了加数个数和目标值,且要求各加数不相等。

● 代码,成套方法系列,2 ms 。

 class Solution
{
public:
std::vector<std::vector<int> > combinationSum3(int k, int n)
{
std::vector<std::vector<int> > res;
std::vector<int> combination;
sum(n, res, combination, , k);
return res;
}
void sum(int target, std::vector<std::vector<int> > &res, std::vector<int> &combination, int begin, int need)
{
if (!target)
{
res.push_back(combination);
return;
}
else if (!need)
return;
for (int i = begin; i < && target >= i * need + need * (need - ) / ; i++)
// 要求 need 个数和为 target,最小的加数 i 满足 target >= i * need(不大于平均数),超过 target / need 的选择被剪枝
// 剩余 (need - 1) 个数的和最小为 1 + 2 + ... + (need-1) = need * (need - 1) / 2,阻止 i 选择过大
{
combination.push_back(i);
sum(target - i, res, combination, i + , need - );
combination.pop_back();
}
}
};

● 大佬的代码,4 ms,算法与上面相同。

 class Solution
{
public:
vector<vector<int>> combinationSum3(int k, int n)
{
vector<vector<int>> res;
vector<int> solu;
sum(res, solu, k, n);
return res;
}
void sum(vector<vector<int>> & res, vector<int> solu, int k, int n)
{
if (solu.size() == k && n == )
res.push_back(solu);
if (solu.size() < k)
{
for (int i = (solu.size() == ? : solu.back() + ); i <= ; i++)
{
if (i > n) break;
solu.push_back(i);
sum(res, solu, k, n - i);
solu.pop_back();
}
}
}
};

▶ 377. 数组元素可以重复使用,要求计算解的个数(包括交换两个不相等元素构成的新解)

● 代码,初版,476 ms 。智障的动态规划,使用两个向量组交替求解从数组最小元素到目标值之间各整数的拆分方法数。

 class Solution
{
public:
int combinationSum4(vector<int> &candidates, int target)
{
if (candidates.size() == )
return ;
sort(candidates.begin(), candidates.end());
if (target < candidates[])
return ; vector<vector<int>> table;
table.push_back(vector<int>(target + , ));
table.push_back(vector<int>(target + , ));
int i, j, k, count;
for (i = count = ; i < candidates.size() && candidates[i] <= target; i++)
table[][candidates[i]] = ;
count += table[][target]; // candidates 本身包含了 target
for (i = ; i <= (target - ) / candidates[] + ; i++) // 填表,循环次数控制
{
table[(i + ) % ].assign(target + , ); // 擦除目标行
for (j = candidates[] * i; j <= target; j++)
{
for (k = ; k < candidates.size() && candidates[k] <= target; k++)
{
if (j - candidates[k] >= && j - candidates[k] <= target)
table[(i + ) % ][j] += table[i % ][j - candidates[k]];
}
}
count += table[(i + ) % ][target];
}
return count;
}
};

● 大佬的代码,3 ms,正确的动态规划姿势,仅在一个向量中进行递推。

 class Solution
{
public:
int combinationSum4(vector<int>& nums, int target)
{
vector<int> t(target + , );
for (int i = ; i <= target; i++)
{
for (auto n : nums)
{
if (n == i)
t[i]++;
else if (n < i)
t[i] += t[i - n];
}
}
return t[target];
}
};

39. Combination Sum + 40. Combination Sum II + 216. Combination Sum III + 377. Combination Sum IV的更多相关文章

  1. Leetcode之回溯法专题-216. 组合总和 III(Combination Sum III)

    Leetcode之回溯法专题-216. 组合总和 III(Combination Sum III) 同类题目: Leetcode之回溯法专题-39. 组合总数(Combination Sum) Lee ...

  2. LeetCode 216. 组合总和 III(Combination Sum III)

    题目描述 找出所有相加之和为 n 的 k 个数的组合.组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字. 说明: 所有数字都是正整数. 解集不能包含重复的组合. 示例 1: 输入 ...

  3. Leetcode之回溯法专题-40. 组合总和 II(Combination Sum II)

    Leetcode之回溯法专题-40. 组合总和 II(Combination Sum II) 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使 ...

  4. 40. 组合总和 II + 递归 + 回溯 + 记录路径

    40. 组合总和 II LeetCode_40 题目描述 题解分析 此题和 39. 组合总和 + 递归 + 回溯 + 存储路径很像,只不过题目修改了一下. 题解的关键是首先将候选数组进行排序,然后记录 ...

  5. 2sum、3sum、4sum以及任意连续的数的和为sum、任意连续或者不连续的数的和为sum

    2sum 如果数组是无序的,先排序(n*logn),然后用两个指针i,j,各自指向数组的首尾两端,令i=0,j=n-1,然后i++,j--,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a ...

  6. Java实现 LeetCode 40 组合总和 II(二)

    40. 组合总和 II 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的每个数字在 ...

  7. Java实现 LeetCode 216. 组合总和 III(三)

    216. 组合总和 III 找出所有相加之和为 n 的 k 个数的组合.组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字. 说明: 所有数字都是正整数. 解集不能包含重复的组合. ...

  8. 216. 组合总和 III

    216. 组合总和 III 题意 找出所有相加之和为 n 的 k 个数的组合.组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字. 说明: 所有数字都是正整数. 解集不能包含重复的 ...

  9. [Leetcode 40]组合数和II Combination Sum II

    [题目] Given a collection of candidate numbers (candidates) and a target number (target), find all uni ...

随机推荐

  1. Spring中的@Transactional

    spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题. 一般使用是通过如下代码对方法或接口或类注释: @Transactiona ...

  2. 牛客网——E进阶吧阶乘

    链接:https://www.nowcoder.net/acm/contest/75/E来源:牛客网 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 32768K,其他语言65536K ...

  3. HTML5安全:CORS(跨域资源共享)简介【转】

    前言:像CORS对于现代前端这么重要的技术在国内基本上居然很少有人使用和提及,在百度或者Google上搜索CORS,搜到的中文文章基本都是 另外一种卫星定位技术CORS的介绍,让我等前端同学情何以堪( ...

  4. Android sdk 更新后编译不过,【Could not find com.android.sdklib.build.ApkBuilderMain】

    最近更新了Android sdk,发现编译不过了 解决方案: 进入 sdk/tool/lib/ 目录下,看看有没有 sdklib.jar 这个文件,如果没有看看有没有sdklib-25.*.*.jar ...

  5. 汉诺塔的c++实现

    void hanNuoTa(int n,int a,int b,int c) { ) return; hanNuoTa(n - , a, c, b); cout << n << ...

  6. Python selenium chrome 环境配置

    Python selenium chrome 环境配置 一.参考文章: 1. 记录一下python easy_install和pip安装地址和方法 http://heipark.iteye.com/b ...

  7. LightOJ - 1396 :Palindromic Numbers (III)(逐位确定法)

    Vinci is a little boy and is very creative. One day his teacher asked him to write all the Palindrom ...

  8. python print 控制台输出中文

    在pycharm里面的控制台用print输出信息,  本意想输出中文, 但是实际上是u\xxxx. 可以用这种方式: print("%s " % cn_string)

  9. streamsets stream selector 使用

    stream selector 就是一个选择器,可以方便的对于不同record 的数据进行区分,并执行不同的处理 pipeline flow stream selector 配置 local fs 配 ...

  10. Spring Cloud 入门 之 Eureka 篇(一)

    原文地址:Spring Cloud 入门 之 Eureka 篇(一) 博客地址:http://www.extlight.com 一.前言 Spring Cloud 是一系列框架的有序集合.它利用 Sp ...