leetcode698题,划分为k个相等的子集。主要是found函数的精妙的回退grp-=nums[start]
leetcode40题,两数相加II。在判断OK的地方保存结果
leetcode322题,零钱兑换。有个妙处是amount/coins[i],一下转换了思路。
leetcode93题,复原IP地址。在判断OK的地方保存结果,另外也要处理回退。其实对一个char处理回退,有个更好的方式是记录上次的end,tmpret[end] = '\0’
主要思路介绍:
1、快排对数据进行整理(贪心),调用一个函数dfs()
2、dfs的结构:退出判断,剪枝判断,for循环, dfs()递归
3、dfs的参数要求:包含有收敛条件相关的参数,包含start参数
4、退出判断:if start == xxx结束
5、剪枝判断:xxx return掉
6、for循环,有时候在这里也进行剪枝。
7、操作+dfs+回退操作
摘自百科:
回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。 用回溯算法解决问题的一般步骤:
1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
3 、以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。
698题目要求:对一个数组 [4, 3, 2, 3, 5, 2, 1]划分成4组,使得每组的和都等于5,是否存在。
因此需要个grpSum来记录每组当前和是多少
函数解释:
1、只有start是变量,去试遍每个组,target是收敛条件,但是这道题目不需要变化。
2、如果满足要求,对start+1递归,即可实现对arr中的每个元素的遍历
3、回退操作:如果当前路径不可以找到最后答案,那么对grpSum进行回退操作
bool dfs(int *nums, int numsSize, int start, int grpnum, int target)
{
if (start == numsSize) {
return true;
}
int i;
for (i = 0; i < grpnum; i++) {
if (grp[i] + nums[start] > target) {
continue;
}
grp[i] += nums[start];
bool a = dfs(nums, numsSize, start + 1, grpnum, target);
if (a == true) {
return true;
}
grp[i] -= nums[start];
}
return false;
}
40题目要求:对一个数组[10,1,2,7,6,1,5],记录所有可以等于8的组合,并返回出来所有可能的结果。
函数解释:
1、start是起始地址,target是收敛条件的参数。选中start以后,去尝试后面start+1。存储某类结果,嘻嘻,已经用全局变量g_len替换了,因为想统一格式
2、选中的start如果上次选过,就不去尝试了(去重)
3、如果当前值小于目标值,就给g_tmp加上当前元素
4、如果当前值等于目标值,target == 0,就给g_ret[g_count]填上g_tmp
5、同样对g_tmp有恢复操作
void dfs(int* candidates, int candidatesSize, int start, int target) {
if (0 == target) {
//满足条件,直接输出。
g_ret[g_count] = (int*)malloc(sizeof(int)*g_len);
if (g_ret[g_count] == NULL) {
return;
}
memcpy(g_ret[g_count], g_tmp, g_len * sizeof(int));
g_col[g_count] = g_len;
g_count++;
}
if (start == candidatesSize) {
return;
}
int i, curr, pre;
pre = 0;
for (i = start; i < candidatesSize; i++) {
curr = candidates[i];
if (pre != 0 && curr == pre) {
//去重
continue;
}
pre = curr;
if (candidates[i] > target) {
//当前元素不满足条件
continue;
}
g_tmp[g_len++] = candidates[i];
dfs(candidates, candidatesSize, i + 1, target - candidates[i]);
g_len--;
}
return;
}
322题目要求:对一个币[1,2,5],目标是11,如何获取最少硬币数,答案是5+5+1
函数解释:
1、start是起始索引,amount是收敛条件的参数;因为是用的除法,如果选中合适的start以后,可以直接尝试start+1了,保证继续执行。
2、如果当前次数大于等于mincount了,就不需要尝试了(剪枝),一定要break,不然会超时
3、如果当前已经满足要求了,就看是否当前g_count比最小值小,进行替换
4、同样对g_count有回退操作
int g_count;
int g_mincount;
void dfs(int* coins, int coinsSize, int start, int amount) {
//printf("%d %d %d %d\n", start, amount, g_mincount, g_count);
if (amount == 0) {
if (g_count < g_mincount){
g_mincount = g_count;
}
return;
}
if (start == coinsSi