C算法-贪心/回溯/剪枝

本文通过分析LeetCode中的4道题目,讲解贪心算法和回溯算法在解决实际问题中的应用。涉及题目包括698题(划分为k个相等的子集)、40题(两数相加II)、322题(零钱兑换)和93题(复原IP地址)。重点介绍了如何构建解空间、进行深度优先搜索、剪枝操作以及回溯操作。同时,给出了各个题目解题的关键思路和代码片段,帮助理解这两种算法的实战技巧。
摘要由CSDN通过智能技术生成

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值