LeeCode Combination Sum 更新动态规划法

原创 2013年12月05日 08:54:40

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] 

 

Because it is not confined to a specific length of the answers, so we cannot kown that before testing, which means there are not fixed end point of the backtracking.

We have to prun(剪枝) the branch(分支), and backtrack to the last number.

It work like this:

1 We sort the numbers in non-descendant way

排序

2 Add the smallest number first, and loop to add it again, and again, until we find a solution, or the solution's value is larger than our target number.

迭代或递归求解

3 We backtrack, by pop up the number , to the last number.

回溯

4 and then we try the next number in candidates, loop to 2.

The key thoughts to modle the solution are: 

1 we use pushing number in and popping out to stand for backtracking thought.

回溯思想由push和pop体现

2 How many numbers in our solution stand for how deep level we iterate or recur.

3 we stop further iterate or recur by compare the number value in solution to the target number.

4 and further more, by knowing the way we arrange our candidates in a non-desendant way, we know if the current number won't fit for the answer, that the rest of the number in candidates won't fit too, so we don't need to try the rest. That's also the thought popular in AI, which call Heuristic Searching.

经验剪枝法

How we deal with repeated answers?

We just forward trying, don't look back the numbers in candiates.

At first, I was wondering whether we can do it in a iterative(迭代循环)way.

So I try, and it work. Below is my iterative program:

	vector<vector<int> > combinationSum1(vector<int> &candidates, int target) 
	{
		sort(candidates.begin(), candidates.end());
		vector<int> intermediate;
		vector<vector<int> > result;
		vector<int> indices;
		for (size_t i = 0; i < candidates.size(); ) 
		{
			if(target <= candidates[i])
			{
				//找到结果,保存,但是不入栈
				if (target == candidates[i])
				{
					intermediate.push_back(candidates[i]);
					result.push_back(intermediate);
					intermediate.pop_back();
				}
				//注意:特殊情况-没有解或一个解,数字最小的值都大于等于target
				if(intermediate.empty()) return result;
				i = indices.back()+1;
				//注意:别忘了target也要恢复上一层的数值
				target += intermediate.back();
				intermediate.pop_back();  // 本层改数值不符合规定,或者已经找到了答案
				indices.pop_back();
				
				while (i == candidates.size())
				{//别忘了这个情况
					//注意:都需要判断空栈的时候返回值
					if(intermediate.empty()) return result;
					i = indices.back()+1;
					target += intermediate.back();
					intermediate.pop_back();  
					indices.pop_back();
				}
			}
			else 
			{
				intermediate.push_back(candidates[i]);
				indices.push_back(i);
				target -= candidates[i];
			}
		}
		return result;
	}


 But the program above is a little bit too complicate. And I think to a problem similar to this, we'd better use recursive way to solve it. So below is the recursive way.

http://discuss.leetcode.com/questions/61/combination-sum

	vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
		sort(candidates.begin(), candidates.end());
		vector<vector<int> > result; // 最终结果
		vector<int> intermediate; // 中间结果
		dfs(candidates, target, 0, intermediate, result);
		return result;
	}

	//不定深度的搜索,最好还是用递归
	void dfs(vector<int>& nums, int gap, int start, vector<int>& intermediate,
		vector<vector<int> > &result) 
	{
		if (gap == 0) {  // 找到一个合法解
			result.push_back(intermediate);
			return;
		}
		for (size_t i = start; i < nums.size(); i++) { // 扩展状态
			//优化剪枝,如:{2,3,6,7} target=7;那么到了2,3之后7-2-3=4,nums[i]=6
			//这样4<6那么6和7都不用继续下面的计算了,直接返回就可以了。
			//这里是最好的剪枝位置了。
			if (gap < nums[i]) return; // 剪枝

			intermediate.push_back(nums[i]); // 执行扩展动作
			dfs(nums, gap - nums[i], i, intermediate, result);
			intermediate.pop_back();  // 撤销动作
		}
	}


 

2013.12.30 Update Dynamic programming algorithm. 

更新动态规划法。

 It should run faster, but we need to construct outcomes with backtracking, so actually it run just a little bit faster than using backtracking directly.

比直接使用回溯法快那么一点点。

It's a good algorithm.

I haven't seen any other website post such kind of dynamic programming approach to this problem yet.

It did have some website also use dynamic programming too, but they are not using thie algorithm like mine.

网上也有其他人写了动态规划法,但是我所看到的跟我的都不一样,有人的动态规划法比直接使用回溯慢, 我的快一点。


class Solution {
public:
	vector<vector<int> > combinationSum(vector<int> &candidates, int target) 
	{
		sort(candidates.begin(), candidates.end());
		vector<vector<int> > ta = genTable(candidates, target);
		vector<vector<int> > res;
		vector<int> tmp;
		
		conBtrack(ta, candidates, res, tmp, candidates.size(), target);

		return res;
	}

	vector<vector<int> > genTable(vector<int> &can, int tar)
	{
		int n = can.size();
		vector<vector<int> > ta(n+1, vector<int>(tar+1));

		for (int i = 0; i < n+1; i++)
		{
			ta[i][0] = 1;
		}

		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= tar; j++)
			{
				if (j >= can[i-1])
				{
					ta[i][j] = ta[i-1][j] + ta[i][j-can[i-1]];
				}
				else
				{
					ta[i][j] = ta[i-1][j];
				}
			}
		}
		return ta;
	}

	//很难写的递归回溯法
	void conBtrack(const vector<vector<int> > &ta, const vector<int>&can,
		vector<vector<int> > &res, vector<int> &tmp, int n, int m)
	{
		while (n>0 && ta[n][m] == ta[n-1][m]) n--;
		if (!n || m <0) return;
		tmp.push_back(can[n-1]);

		conBtrack(ta, can, res, tmp, n, m-can[n-1]);
		if (m-can[n-1]==0) 
		{
			res.push_back(tmp);
			reverse(res.back().begin(), res.back().end());
		}
		tmp.pop_back();
		conBtrack(ta, can, res, tmp, n-1, m);
	}
};


2014-1-26 update

class Solution {
public:
    vector<vector<int> > combinationSum(vector<int> &candidates, int target) 
	{
		sort(candidates.begin(), candidates.end());
		vector<vector<int> > rs;
		vector<int> tmp;
		comb(rs, tmp, candidates, target);
		return rs;
	}

	void comb(vector<vector<int> > &rs, vector<int> &tmp,
		vector<int> &can, int tar, int index=0)
	{
		if (!tmp.empty() && tar == 0)
		{
			rs.push_back(tmp);
		}
		for (int i = index; i < can.size(); i++)
		{
			if (tar >= can[i])
			{
				tmp.push_back(can[i]);
				comb(rs, tmp, can, tar-can[i], i);
				tmp.pop_back();
			}
		}
	}
};


 

 

版权声明:本文作者靖心,靖空间地址:http://blog.csdn.net/kenden23/,未经本作者允许不得转载。

leetCode 39.Combination Sum(组合总和) 解题思路和方法

Combination Sum Given a set of candidate numbers (C) and a target number (T), find all unique com...
  • xygy8860
  • xygy8860
  • 2015年07月09日 22:33
  • 1149

回溯详解及其应用:Leetcode 39 combination sum

原理 初入门 基本定义和概念 举栗子 编程思路 实践 生成符合规范的括号 combination sum n queen原理初入门有时会遇到这样一类题目,它的问题可以分解,但是又不能得出明确的动态规划...
  • c602273091
  • c602273091
  • 2017年01月31日 16:47
  • 346

【LeetCode-面试算法经典-Java实现】【216-Combination Sum III (组合数的和)】

【216-Combination Sum III (组合数的和)】【LeetCode-面试算法经典-Java实现】【所有题目目录索引】代码下载【https://github.com/Wang-Jun-...
  • DERRANTCM
  • DERRANTCM
  • 2015年08月28日 06:58
  • 3545

Cydia 出现“Hash Sum mismatch”报错解决方法

转:http://jb.appvv.com/news/19829.shtml "Hash Sum mismatch",哈希校验值总和不匹配。Cydia 常见报错之一。出现此情况后,再次下载时会从头开...
  • zhangmiaoping23
  • zhangmiaoping23
  • 2015年07月10日 14:39
  • 6195

【回溯】【leetcode题解】【M】【57】Combination Sum

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C wher...
  • sscssz
  • sscssz
  • 2015年12月15日 18:37
  • 200

LeeCode编程训练日记一:Two Sum

通过Leecode训练编程,每天只做一个题目,重在训练编程思想 Practice Day 1: 2017/06/25  参考:http://blog.csdn.net/murdock_c/ar...
  • sinat_28940673
  • sinat_28940673
  • 2017年06月26日 16:39
  • 90

LeetCode OJ算法题(三十八):Combination Sum

题目: Given a set of candidate numbers (C) and a target number (T), find all unique combinations ...
  • op_yu
  • op_yu
  • 2014年07月25日 12:10
  • 376

LeetCode第39题之Combination Sum(两种方法)

思路:两种方法都是利用递归回溯,第二方法在第一种方法的基础上对原始数据先进行排序,这样可以剪枝,加快计算速度。第一种方法在LeetCode上测试运行的时间是24ms,第二种方法运行时间为16ms。 ...
  • u011954296
  • u011954296
  • 2016年06月18日 12:27
  • 180

每日算法之三十一:Combination Sum

给定一个整数序列,求解一个子序列,子序列之和等于给定目标值。子序列满足以下条件: 1)子序列是有序的 2)子序列的元素个数不限,可以是给定元素的重复元素。 3)结果中的子序列是唯一的 原题描述...
  • yapian8
  • yapian8
  • 2014年06月08日 22:00
  • 680

39. Combination Sum 求目标和的元素集合

39. Combination Sum 求目标和的元素集合
  • u014274339
  • u014274339
  • 2017年03月20日 08:04
  • 217
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:LeeCode Combination Sum 更新动态规划法
举报原因:
原因补充:

(最多只允许输入30个字)