【算法】动态规划(二)打家劫舍问题

问题:给出一个序列,选出不相邻的的序列的和的最大值。

 例如,有如下一个序列:

1241783

如果我们选择了第一个数,其价值为1,那么我们就不能选择2;当我们选择了7,那么其左右两边的数1和8都不能选择,现在任意给出一格序列,求其中可选的数的和的最大值。

涉及到动态规划问题,我们就要找重复的子问题和最优的子结构。

这个问题,对于每个数,我们有选或者不选两种选择方式。我给出了个数组OPT,每个值代表前N个数所能选的最大值的和。例如,OPT[7], 当我们选择7号数字的时候,那么6号数字8就不能选了,所以如果选择了7号数字,那么我们当前最大的值就是value[7]+OPT[5]. 如果不选,那么当前最大值就是钱6个数字之间的最大值OPT[6]. 对于每个数字,我们都有如下操作。其递归示意图如下所示:

图中左右子树都出现了OPT[5]和OPT[4] ,为了在计算时不重复计算相同的值,我们将每次的OPT[i] 计算后保存起来,这样在计算大于i的数的时候,就可以使用了。这也是动态规划用空间换时间的思想。

根据之前的思路,我们可以得到前N个数的可选最大值,但是对于递归的开始还需要做额外的定义:当只有一个数的时候,那么可选的只有它,最大值也是它,当只有两个数的时候,可选的就是两个数中最大的数。当总数大于三的时候,我们就可以根据前面的最大值进行递归计算了。

当然,有兴趣也可以计算每次最大值选择的数字。

该题目的公式如下:

OPT[n] =\left\{\begin{matrix} MAX(OPT[N-2]+Value[n],OPT[n-1]) & n>2 \\ OPT[0] == Value[0];OPT[1] = Max(Value[0],Value[1]) & n=1,2 \end{matrix}\right.

本题目的JAVA实现算法如下,其实本体很简单,但是我加入了计算每次的选择的数字的集合,就显得有点繁琐了。

import java.util.*;
/*
 * 打家劫舍问题:
 * 给出一段序列:比如比,2,4,1,7,8,3
 * 如果选了一个,就不能选其周围的,比如选了4,那么2,1,就不能选
 * 给出最大的选择权值和
 * 以及给出最大权值和的序列下标
 * */
public class DJSolution {
	List<List<String>> list;
	int[] opt,q;
	
	DJSolution(int[] value){
		opt = new int[value.length];
		q = value;
	}
	
	public boolean MaxValue() {
		if(q.length==0)
			return 	false;
		else if(q.length==1) {
			this.opt[0] = q[0];
			return true;
		}
		else if(q.length==2) {
			opt[0] = q[0];
			opt[1] = Math.max(q[0], q[1]);
			return true;
		}
		else {
			list = new ArrayList<List<String>>();
			opt[0] = q[0];
			opt[1] = Math.max(q[0], q[1]);	
			List<String> q1 = Arrays.asList("1 "+String.valueOf(q[0]));
			List<String> q2;
			if(q[0]>=q[1]) 
				q2 = Arrays.asList("1 "+String.valueOf(q[0]));
			else
				q2 = Arrays.asList("2 "+String.valueOf(q[1]));	
			list.add(q1);
			list.add(q2);
			Compute();
			return true;
		}
	}
	
	public int Compute() {
		for(int i = 2;i<q.length;i++) {
			List<String> res = new ArrayList<String>();
//			opt[i] = Math.max(opt[i-2]+q[i], opt[i-1]);
			if(opt[i-2]+q[i]>=opt[i-1]) {
				opt[i] = opt[i-2]+q[i];
				res.addAll(list.get(i-2));
				res.add(String.valueOf(i+1)+" "+String.valueOf(q[i]));
				list.add(res);
			}
			else {
				opt[i] = opt[i-1];
				res.addAll(list.get(i-1));
				list.add(res);
			}
		}		
		System.out.println("前N和任务的最大值:"+Arrays.toString(opt));
		System.out.println("总任务最大值:"+opt[opt.length-1]);
		System.out.println("总任务最大值选择:"+list.get(list.size()-1).toString());
		return opt[opt.length-1]; 	
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] q = {1,2,5,3,3,6,2,1,6,4,4,6,3,5,7};
		new DJSolution(q).MaxValue();
	}

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值