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

▶ 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. xargs -i的用法

    find ... | 后面跟文件,就需要用到xargs,其中-i加{}就能替代管道符之前的文件(标准输出的内容) 在/tmp下创建6个文件,1.txt,2.txt,3.txt,4.txt,5.txt, ...

  2. HDU - 4812 D Tree 点分治

    http://acm.hdu.edu.cn/showproblem.php?pid=4812 题意:有一棵树,每个点有一个权值要求找最小的一对点,路径上的乘积mod1e6+3为k 题解:点分治,挨个把 ...

  3. React中使用UEditor

    一般UEditor用于表单的新建和编辑 <FormItem {...formItemLayout} label='商品详情'> {getFieldDecorator('detail', { ...

  4. html中元素盒子垂直居中的实现方法

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  5. idea常用实用快捷键

    Ctrl+Alt+方向键(左键,右键),返回上次查看的位置.(这个快捷键和window本身快捷键冲突,需要关闭windows 对应快捷键功能,参考博客:https://blog.csdn.net/u0 ...

  6. 源码编译运行android emulator

    source buile/envsetup.sh lunch sdk-eng make sdk -j2 编译完之后,sdk安装在了下面的目录里 ANDROIID_DIR/out/host/linux- ...

  7. ASP.NET FORM认证配置排错记录

    搞了2小时都不能实现自动跳转到登录页面,后删除了配置文件中的name,就解决问题了. <authorization>      <deny users="?" / ...

  8. winform messagebox 统一

    vb.net 里面有两种messagebox,一种是vb语言提供的msgbox,另一种是.net framework 提供的messagebox.在此统一使用messagebox. Warning,提 ...

  9. 认识 ARM、FPGA

    0. ARM ARM:Advanced RISC machine,微处理行业的一家知名企业.适用于多种领域,如嵌入控制,消费.教育类多媒体.DSP和移动式应用. 优势: 功耗低,不容易发热.死机: 3 ...

  10. vue项目之webpack打包静态资源路径不准确

    摘自:https://blog.csdn.net/viewyu12345/article/details/83187815 问题 将打包好的项目部署到服务器,发现报错说图片找不到. 静态资源如js访问 ...