动态规划(二)

本次通过两个简单的算法题来进一步练习和理解动态规划,废话不多说,来上题吧。

题目一

给出一个数组,如[6,1,1,9,3]在里面取任意个数,要求所取得数的位置不能相邻,比如取了第一个数就不能取第二个数,但是可以取第三第四个数,求取出数之和的最大值。
下面来让我们分析一下这个题,我们可以分解成求每一位的最优解,从前往后查,如果要求第n个数的最优解,那么只能有两种情况:
第一种情况是 取第n个数和第n+2个数的最优解
第二种情况是 取第n+1个数的最优解
因此fib( n ) = max ( arr[n] + fib(n-2) , fib(n-1) ),下面我们用递归来解决一下这个问题:

递归
static void Main()
{
	int[] arr = { 6, 1, 1, 9, 3 };
	Console.WriteLine(max_sum(0, arr));
	Console.Read();
}
static int max_sum(int index, int[] arr)
{
	if (index == arr.Length - 1)
		return arr[index];
	if (index == arr.Length - 2)
		return arr[index] > arr[index + 1] ? arr[index] : arr[index + 1];
	int A = max_sum(index + 2, arr) + arr[index];
	int B = max_sum(index + 1, arr);
		return A > B ? A : B;
}

结果为15,即取得数为6和9.
但是用递归的方法依然会出现上一篇中重复计算的现象,时间复杂度是O(2^n)。
解决方法同样和上一篇中的一样,只需把计算过的结果记录下来,用到时直接调用即可。

自顶而下
static void Main()
{
	int[] arr = { 6, 1, 1, 9, 3 };
	Dictionary<int, int> index_max = new Dictionary<int, int>();
	Console.WriteLine(max_sum(0, arr, index_max));
	Console.Read();
}
static int max_sum(int index, int[] arr, Dictionary<int, int> index_max)
{
	int A, B;
	if (index == arr.Length - 1)
		return arr[index];
	if (index == arr.Length - 2)
		return arr[index] > arr[index + 1] ? arr[index] : arr[index + 1];
	if (index_max.ContainsKey(index + 2))
		A = index_max[index + 2] + arr[index];
	else
	{
		A = max_sum(index + 2, arr, index_max);
		index_max.Add(index + 2, A);
        A += arr[index];
	}
	if (index_max.ContainsKey(index + 1))
		B = index_max[index + 1];
	else
	{
		B = max_sum(index + 1, arr, index_max);
		index_max.Add(index + 1, B);
	}
		return A > B ? A : B;
}
自底而上
static void Main()
{
	int[] arr = { 6, 1, 1, 9, 3 };
	int[] opt = new int[arr.Length];
	opt[0] = arr[0];
	if (arr[1] > opt[0])
		opt[1] = arr[1];
	else
		opt[1] = opt[0];
	for (int i = 2; i < arr.Length; i++)
		opt[i] = arr[i] + opt[i - 2] > opt[i - 1] ? arr[i] + opt[i - 2] : opt[i - 1];
	Console.WriteLine(opt[arr.Length - 1]);
	Console.Read();
}

题目二

这个题我在leetcode上练习时遇到的,虽然不难,但是容易陷入思维误区。
数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 cost[i](索引从0开始)。每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
示例 1:
输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。

示例 2:
输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。

注意:
cost 的长度将会在 [2, 1000]。
每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。

这个题是不是和上一题很像?
上一个题是fib(n)=max(arr[n]+fib(n-2),fib(n-1))求的是最大值,这个题求得是最小值是不是应该是fib(n)=min(arr[n]+fib(n-2),fib(n-1))?
这和我一开始想的一样的,但实际上这样是不对的,如果按照这个思路来写只会输出最初的最小值,你可以自行写一下看看。
这个题实际上是fib(n)=min(fib(n-1),fib(n-2))+arr[n]
因为最后一个台阶是必要走的,可以这样理解,如果只有一个台阶,那这个台阶是必须要走的。

static void Main()
{
	int[] cost = { 1, 100, 1, 1, 1, 100, 1, 1, 100, 1 };
	int[] opt = new int[cost.Length+1];
	opt[0] = 0;
	opt[1] = cost[0];
	for (int i = 1; i < cost.Length; i++)
		opt[i + 1] = (opt[i] < opt[i - 1] ? opt[i] : opt[i - 1]) + cost[i];
	Console.WriteLine(opt[cost.Length] < opt[cost.Length - 1] ? opt[cost.Length] : opt[cost.Length - 1]);
	Console.Read();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值