所谓贪心算法是指,在对问题求解时,老是作出在当前看来是最好的选择。也就是说,不从总体最优上加以考虑,他所作出的仅是在某种意义上的局部最优解。java
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对全部问题都能获得总体最优解,选择的贪心策略必须具有无后效性,即某个状态之后的过程不会影响之前的状态,只与当前状态有关。c++
2. 把求解的问题分红若干个子问题。算法
3. 对每一子问题求解,获得子问题的局部最优解。数组
4. 把子问题的解局部最优解合成原来解问题的一个解。框架
贪心算法适用的前提是:局部最优策略能致使产生全局最优解函数
实际上,贪心算法适用的状况不多。通常,对一个问题分析是否适用于贪心算法,能够先选择该问题下的几个实际数据进行分析,就可作出判断。
spa
其实该状况是符合贪心策略的,由于该总状况无论先选哪两个都会把背包塞满,由于该题物品能够分割成任意大小,因此,就算空下一下,也能够将最后一个物品分割,放进去,它们的单位重量的价值是同样的,因此,最后背包最后重量相同,重量相同那么价值也相同。)设计
因此采用第三种策略,代码以下:code
package cn.itcast.recursion; import java.util.Arrays; public class GreedyPackage { private int MAX_WEIGHT = 150; private int[] weights = new int[]{35, 30, 60, 50, 40, 10, 25}; private int[] values = new int[]{10, 40, 30, 50, 35, 40, 30}; private void packageGreedy(int capacity, int weights[], int[] values) { int n = weights.length;//物品的数量
double[] r = new double[n];//性价比数组
int[] index = new int[n];//性价比排序物品的下标
for (int i = 0; i < n; i++) { r[i] = (double) values[i] / weights[i]; index[i] = i;//默认排序
} double temp = 0;//对性价比进行排序
for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { //降序,对性价比和对应下标进行排序
if (r[i] < r[j]) { temp = r[i]; r[i] = r[j]; r[j] = temp; int x = index[i]; index[i] = index[j]; index[j] = x; } } } //排序好的重量和价值分别存到数组
int[] w1 = new int[n]; int[] v1 = new int[n]; //排序好的重量和价值分别存到数组
for (int i = 0; i < n; i++) { w1[i] = weights[index[i]]; v1[i] = values[index[i]]; } //用来装物品的数组
int[] x = new int[n]; //放入物品的最大价值
int maxValue = 0; //放入物品的总重量
int totalweights = 0; for (int i = 0; i < n; i++) { //物品重量比包的总容量小,表示还能够装得下
if (w1[i] < capacity) { x[i] = 1;//表示该物品被装了
maxValue += v1[i]; System.out.println(w1[i] + "kg的物品被放进包包,价值:" + v1[i]); totalweights += w1[i]; capacity = capacity - w1[i]; } } System.out.println("总共放入的物品数量:" + Arrays.toString(x)); System.out.println("总共放入的物品总重量" + totalweights); System.out.println("放入物品的最大价值:" + maxValue); } public static void main(String[] args) { GreedyPackage greedyPackage = new GreedyPackage(); greedyPackage.packageGreedy(greedyPackage.MAX_WEIGHT, greedyPackage.weights, greedyPackage.values); } }
将原问题划分红n个规模较小,而且结构与原问题类似的子问题,递归地解决这些子问题,而后再合并其结果,就获得原问题的解。blog
“分而治之”,大问题可以拆成类似的小问题,记住这些小问题须要具备类似性。然后将小问题的每一个解合成为大问题的解。因此说大问题如何拆,小问题如何合并才是这个算法最主要的一个思想。实际上不少算法如贪心算法,动态规划等等都是要求把大问题拆成小问题。而分治算法的重要一点就是要适用于可以从新把小问题的解合并为大问题的解。
一、该问题的规模缩小到必定程度就能够很容易解决;
二、该问题能够分解为若干个规模较小的相同问题,这里注意是最优子结构性质;
三、利用该问题分解出的子问题的解能够合并为该问题的解;
四、该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共子问题;
对于不少算法而言,第一条每每是必要的,由于数据量一旦大起来,问题每每复杂度上升的特别快。这里就须要将这个大问题分解为小问题。小问题处理起来更加方便。第2、三条的才是分治思想的核心,由于不少时候咱们会采用递归的方式进行解决,因此在大问题分解为小问题的时候须要保证小问题之间的相同性。单单分解为小问题以后还不能算完成,必需要可以将小问题的解合并为这个问题的最终解才能算真正用到了分治的思想。最后一条也是最关键的,各个子问题之间必需要保证独立性,即不互相影响。若是相互之间有影响,这时候咱们采用的是动态规划就更加好一点。
其实算法的思想不用讲太多,可以化为几句话是最好的,下面就举几个例子来看看分治算法:
当咱们遇到一个问题,彻底能够在内心问本身下面四个问题:
一、当前问题能不能切分?
答:能切分,由于数组按照升序来排列。因此当x大于某个元素array[mid]时,x必定在array[mid]的右边。以此再来切分。每次切一半
二、分解出来的子问题相同吗?
答:相同,每一个子问题的数据集都是父问题的1/2倍。而且每次只比较子问题的中间的数据
三、子问题的解能合并为父问题的解吗?
答:不须要合并,子问题的解即为父问题的解。
四、子问题之间相互独立吗?
答:独立,子问题只是判断,不须要和父问题有很强的关联性(这里能够参考一下动态规划算法,就能理解子问题之间怎么判断是独立的)
一样在本身内心问问4个问题
一、当前问题能切分吗?
答:能,最简单的就是两个数之间的比较,这个数组能够当作多个两个数来比较
二、分解出来的子问题是否相同?
答:相同,都是两个数比较大小。
三、子问题的解可以合成父问题的解吗?
答:每两个有序数组再按照必定顺序合起来就是最终的题解。这里就是有个合并的过程
四、子问题之间相互独立吗?
答:独立,分到最小的时候子问题之间互不影响。
下面是归并排序代码:
分治算法只是一种思想,不是一个具体的套路,只能说在遇见具体问题时咱们可以从这个思路去思考,切分问题?合并问题?子问题之间影响关联大不大?这些都是具体问题具体考虑。还有不少不少题目是用了分治算法。也能够多刷刷题
设有n=2^k个运动员,要进行网球循环赛。如今要设计一个知足如下要求的比赛日程表
(1)每一个选手必须与其余n-1个选手各赛一场
(2)每一个选手一天只能赛一次
(3)循环赛一共进行n-1天
将比赛日程表设计成n行n列,表中除了第一列,其余n-1列才是咱们要的,数组下标行列都从0开始,第i行j列表明第(i+1)位选手在第j天的对手:
以8个选手为例子,下面是填表的步骤:
①咱们先初始化第一行各个数为1~8(2~8为:第1天 — 第7天);
②由于是递归,那么要填8x8的左下角和右下角,分别须要知道它的右上角和左上角
③而8x8的盒子它的左上角是一个4x4的盒子,要填4x4的左下角和右下角,也分别须要知道它的右上角和左上角
④如今递归到4x4的盒子的左上角,是一个2x2的盒子,它不须要递归了,直接沿对角线填左下角和右下角的数字,也就是上面的图②
⑤能够看到,通过上面的②③步,咱们左上角4x4的盒子,它的·右上角和左上角已经知道了,那就能够沿对角线填它的左下角和右下角了,因此出现了图④
⑥其余的依次类推
通俗易懂地讲,就是若是你想填一个大的,你得先得出它左上角和右上角两个盒子 , 再沿对角线分别抄到右下角和左下角。 而为了得出它左上角和右上角,就须要递归了。
package cn.itcast.recursion; public class SportsSchedule { public void scheduleTable(int[][] table, int n) { if (n == 1) { table[0][0] = 1; } else { /* 填充左上区域矩阵 n值的变化:8 4 2 1 m值的变化:4 2 1 1 */
int m = n / 2; scheduleTable(table, m); //填充右上区域矩阵
for (int i = 0; i < m; i++) { for (int j = m; j < n; j++) { table[i][j] = table[i][j - m] + m; } } //填充左下区域矩阵
for (int i = m; i < n; i++) { for (int j = 0; j < m; j++) { table[i][j] = table[i - m][j] + m; } } //填充右下区域矩阵
for (int i = m; i < n; i++) { for (int j = m; j < n; j++) { table[i][j] = table[i - m][j - m]; } } } } public static void main(String[] args) { int[][] table = new int[8][8]; int n = 8; SportsSchedule schedule = new SportsSchedule(); schedule.scheduleTable(table, n); int c = 0; //打印二维数组
for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { System.out.print(table[i][j] + " "); c++;//每打印一个数,c++
if (c % n == 0) {//说明打印一行了
System.out.println();//换行
} } } } }