在一个月之前,为了准备蓝桥杯比赛,我们学校组织了参加蓝桥杯竞赛的男人和女人们进行集中学习。在这段时间里我和我们学校很多爱学习爱算法的同学们一起进步一起讨论,同时对算法的学习也是从未停止。当我们学到动态规划的时候,很多人就感觉没有贪心算法查并集最短路这么容易理解了,在下对动态规划还是小有理解,所以在这里写下这篇博客。记录自己的学习和进步。
对于动态规划的基本思想,很多书本上是这样解释的:
和分治法相似,动态规划的基本思想就是把带求解的问题分解为若干个小规模的子问题,然后从这些子问题的解演算出待求问题的解。
对于已经理解了动态规划的同学来说,这段话很容易理解,但是对于刚开始接触动态规划的童鞋来说,这是非常抽象的。疑惑点可能有以下几种:
1 什么样的问题可以用动态规划?
2 子问题在待求解的问题中具体是什么?
3 通过子问题如何推算出待求解的问题的解?
首先动态规划使用的问题:
一般来说,规划就是选择,也就是说,当问题的性质是选出最优方案,最优方案的最优值。这类问题可以用动态规划。即条件选择问题。
子问题可以认为就是把一个问题的数量规模不断缩小,直到可以得出最直接的解为止。
对于第三个问题,最好通过代码来理解。所以请看以下例子。
举一个简单的例题:
对于一个长度小于100,每一个单元当中都是一个整数的数组。我们在其中选择若干个数字使我们选择的这些数字的和最大。
选择的规则是,如果我们选择了一个数字,那么被选择的这个数字的相邻单元的数字就不能被选取。------------------------------------------------------------------------------------------------
我们先假设这个数组的长度为1,因为数组中只有一个元素,所以只有一种选择方式。
如果数组长度为2,那么我们就有了两种选择方式,所以为了实现最大值,我们要选择其中较大的数。
如果数组长度为3,那么我们就有很多种方式选择。比如选择第一个和第三个。只选择第一个。只选择第二个。只选择第三个。如果数组长度不断扩大,选择的方式就会越来越多。问题的复杂程度就会越来越大。
所以我们这样考虑:
假设数组长度为100,那么我们的问题就是在前100个数字中选出最优解。
我们先考虑要不要选择最后一个数字。如果选择了最后一个数字就意味着第99个数字不能被选择,那么我们的最终结果就是在前98个数字中选出的最优解加上第100个数的值。如果我们不选第100个数字,那么我们的最终结果就是前99个数字的最优解。
类似的:前99个数字的最优解依然可以分为两个子问题,如果选择第99个数,那么前99最优解就是前97最优解加上第99个数。如果不选择第99个数,那么前99最优解就等于前98最优解。
同样的道理可以重复N遍。
所以我们有以下代码。来解决问题。
public class Main {
//dp函数的意思就是在数组中找出第last_index之前的最优解。
public static int dp(int[] number,int last_index) {//动态规划,在数组number中从第last_index开始向前选择。
if(last_index == 0) {//如果问题规模只有一个数字
return number[0];
}else if(last_index == 1) {//如果问题规模是两个数字
int max = number[0]>number[1]?number[0]:number[1];
return max;
}else {//否则开始递归调用,
int a = dp(number,last_index-2) + number[last_index];//在当前规模问题下,如果选择了第最后一个数
int b = dp(number,last_index-1);//在当前规模问题下,如果没有选择第最后一个数
return Math.max(a, b);
}
}
public static void main(String[] args) {
int[] storage = {3,5,9,1,8,6,4};
int finish = dp(storage,storage.length-1);
System.out.println(finish);
}
}
控制台打印输出24。正好为最优解。