面试题14:剪绳子

// 面试题14:剪绳子
// 题目:给你一根长度为n绳子,请把绳子剪成m段(m、n都是整数,n>1并且m≥1)。
// 每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]*k[1]*…*k[m]可能的最大乘
// 积是多少?例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此
// 时得到最大的乘积18。

解题思路:

书上说的解题办法是动态规划或者贪婪算法。

1)动态规划

动态规划就是把大问题划分为一个一个小的子问题,然后小问题之间还有相互重叠的小问题。

由于子问题在分解大问题的过程中重复出现,我们可以用从下往上的顺序先计算小问题的最

优解并储存起来,再以此为基础求取大问题的最优解。

这个叫从上往下分析问题,从下往上求解问题(听起来好有道理)。

回到这个剪绳子的问题,第一刀切的时候有n-1种选择(1~n-1),由于不知道那种方法是最优解,

所以我们需要把所有可能的情况都尝试一遍,也就是f(n)=max(f(i),f(n-1)),0<i<n且i属于正整数。

2)贪婪算法

应用贪婪算法的时候,每一步都可以做当前最贪婪的选择,

当把长度为n的绳子剪成长度为k和n-k的绳子时,f(k)=k(n-k)-n=-k^2+n*k-n

如果需要保证剪短之后乘积更大,即k(n-k)>n,应该存在k0使得f(k0)>0。

由二次函数图像知:f(k)max=f(k/2)=(n/4-1)*n,也就是说应该在n>=5的时候才应该剪短,否则是得不偿失的。

当n>=5时,有3*(n-3)>n和2*(n-2)>n,且1*(n-1)<n,4*(n-4)<n,结合函数图像不难推广到全体整数,

所以此时应尽可能多的剪成长度为3或者2的绳子段。

那么现在就要用到贪婪算法了,既然只能剪成3或2,那我就全给他剪成3。

但是如果最后剩下的刚好是长度4,剪成了3+1,得不偿失,这个时候需要接回去,再剪成2*2的。(因为至少剪一刀)

 

其实动态规划和贪婪算法是非常重要的算法,比如最小生成树的Kruskal和Prim算法,以及单源多点最短路径的

Dijkstra算法都用到了贪婪算法,就是我差不多全忘完了,现在就还记得名字和大概步骤...

应该早点开始写博客和github把这些东西全记录下来的,唉,都怪当初太年轻。

c/c++:

 

// ====================动态规划====================
int maxProductAfterCutting_solution1(int length) {
	if (length < 2)
		return 0;
	if (length == 2)
		return 1;
	if (length == 3)
		return 2;

	int* products = new int[length + 1];
	products[0] = 0;
	products[1] = 1;
	products[2] = 2;
	products[3] = 3;
	
	//依次寻找从i到length的最优解
	int max = 0;
	for (int i = 4; i <= length; i++) {
		max = 0;

		//寻找i的最优解
		for (int j = 1; j <= i / 2; j++) {
			int product = products[j] * products[i - j];
			if (product > max)
				max = product;
		}

		products[i] = max;
	}
	
	max = products[length];
		
	delete[] products;
	return max;
}
// ====================贪婪算法====================
int maxProductAfterCutting_solution2(int length) {
	if (length < 2)
		return 0;
	if (length == 2) 
		return 1;
	if (length == 3)
		return 2;

	//尽可能多的减去长度为3的绳子
	int timesOf3 = length / 3;

	//如果最后是4剪成了3+1,再给他接回去
	//因为2*2>3*1
	if (length - timesOf3 * 3 == 1) 
		timesOf3 -= 1;
	
	//剩下的都给剪成长度为2的绳子
	int timesOf2 = (length - timesOf3 * 3) / 2;
	
	return (int)(pow(3, timesOf3)) * (int)(pow(2, timesOf2));
}

我猜肯定有人和我想的一样,直接把(int)(pow(2, timesOf2))改写成4多好,还算啥啊,浪费时间。

那你真是too young too simple,sometimes naive。

当绳子长度为5,先剪成3,然后剩下了2,(int)(pow(2, timesOf2))计算的结果就是2而不是4.

虽然就这么一种例外情况,但该写的还是要写滴,当然你修改一下return语句简化计算也不是不可以。

参考资料:

posted @ 2018-08-07 23:18  朕蹲厕唱忐忑  阅读(220)  评论(0编辑  收藏  举报