回溯

今天理解了一点回溯的相关知识,记录一下。

链接:https://www.nowcoder.com/questionTerminal/57d85990ba5b440ab888fc72b0751bf8?answerType=1&f=discussion
来源:牛客网

给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],...,k[m]。请问k[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

看到这道题最开始想到的是,穷举出所有的可能情况,选择最大的,自然而然穷举,最简单的实现还是递归,我们假设,长度为7的绳子,剪一刀,出现的所有可能,分别是(1*6)、(2*5)、(3*4)、(4*3)、(5*2)、(6*1),观察一下,一组值,第一个用for实现,第二个值为总长减去第一个值,问题来了,那么剪完一刀后剩余的部分又该怎么剪,其实很简单,再按照上面的套路继续穷举呗,所以递归初步成型,就差退出条件,我们发现分到绳子还剩4的时候,绳子再剪的话就亏了,但是退出不应该==4的时候退出,因为我们发现绳子长度为5的时候,其实1*4 小于2*3。(通过特例去验证边界条件是最好的方法)。

class Solution 
{
public:
	int back_track(int n) {
		// n <= 4, 表明不分,长度是最大的
		if (n <= 4) {
			return n;
		}

		int ret = 0;
		for (int i = 1; i < n; ++i) {
			ret = max(ret, i * back_track(n - i)); 
		}
		return ret;
	}
	int cutRope(int number) {
		// number = 2 和 3 时,分 2 段和分 1 段的结果是不一样的,所以需要特判一下
		if (number == 2) {
			return 1;
		}
		else if (number == 3) {
			return 2;
		}
		return back_track(number);
	}
};

上面的代码超时了,确实,时间复杂度是n的阶乘。

我们发现,上面有好多重复计算,我们写一个备忘录,免去重复计算。

class Solution {
public:
	int back_track(int n, vector<int> &mark) {
		if (n <= 4) {
			return n;
		}
		// 在方法一的基础上添加
		if (mark[n] != -1) {
			return mark[n];
		}

		int ret = 0;
		for (int i = 1; i < n; ++i) {
			ret = max(ret, i * back_track(n - i, mark));
		}
		// 添加部分
		return mark[n] = ret;
	}
	int cutRope(int number) {
		if (number == 2) {
			return 1;
		}
		else if (number == 3) {
			return 2;
		}
		// 添加部分
		vector<int> mark(number+1, -1);
		return back_track(number, mark);
	}
};

 在这里说一下,递归从函数调用点入栈之后,出栈的时候,基于本层栈的所有变量的值,从调用点之后,执行到函数大括号完,才能完整出栈。发现上面的代码和DFS很相似,按照上面画的递归图人肉执行一次就会发现回溯的奥秘。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值