所謂貪心算法是指,在對問題求解時,老是作出在當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出的僅是在某種意義上的局部最優解。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();//換行
} } } } }