日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划与贪心算法

發(fā)布時間:2024/4/11 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划与贪心算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

動態(tài)規(guī)劃與貪心算法

  • 關于
    • 什么是動態(tài)規(guī)劃
    • 如何使用動態(tài)規(guī)劃
        • 遞歸寫法(記憶型遞歸)
        • 遞推寫法
        • 動態(tài)規(guī)劃與分治區(qū)別
    • 什么是貪心算法
    • 如何使用貪心算法
        • 動態(tài)規(guī)劃與貪心算法區(qū)別

關于

最近常看到動態(tài)規(guī)劃與貪心算法,有一點點小小的心得,寫在這里

動態(tài)規(guī)劃是一種非常精妙的算法,它沒有固定的寫法,常需要具體問題具體分析
貪心算法是一種特殊的動態(tài)規(guī)劃,具體怎么特殊,從下面可以看出

什么是動態(tài)規(guī)劃


動態(tài)規(guī)劃(Dynamic Programming, DP) 是一種用來解決一類最優(yōu)化問題的算法思想

動態(tài)規(guī)劃將一個復雜的問題分解為若干個子問題,綜合子問題的最優(yōu)解來獲得原問題的最優(yōu)解
需要注意的是,動態(tài)規(guī)劃會將每個子問題的解記錄下來,這樣遇到相同的子問題時可以直接使用結果,從而避免重復運算

如何使用動態(tài)規(guī)劃

遞歸寫法(記憶型遞歸)

使用一個額外的數組(dp數組)來記錄算過的值,例如下面的rec數組

/*** 帶備忘錄的fib* * @param n* @return*/public static int fibRecord(int n) {int[] rec = new int[n + 1];int fib = fib(rec, n);return fib;}/*** 帶備忘錄的fib計算核心函數* * @param rec* @param n* @return*/private static int fib(int[] rec, int n) {if (n == 1 || n == 2) {return 1;}if (rec[n] > 0)return rec[n];rec[n] = fib(rec, n - 1) + fib(rec, n - 2);// 記錄return rec[n];}

通過上面的例子我們可以發(fā)現:
如果一個問題可以被分解成若干個子問題,且這個子問題會重復出現,那就稱這個問題有重疊子問題(Overlapping Subproblems)

遞推寫法

例子:

數字三角
尋找一條從頂點到邊的路徑,使得經過的數字和最大
輸入:
triangle = {
{ 7 },
{ 3, 8 },
{ 8, 1, 0 },
{ 2, 7, 4, 4 },
{ 4, 5, 2, 6, 5 }};
輸出:
30
即(7 -> 3 ->8 ->7 ->5)
分析:
如果要求位置[0][0]到底層的最大和, 那么就要先求出它的兩個子問題:1.位置[1][0]到底層的最大和,與2.位置[1][1]到底層的最大和…(即走左邊還是走右邊)
即:
dp[0][0]=tri[0][0]+max(dp[0+1][0],dp[0+1][0+1]);dp[0][0] = tri[0][0] + max(dp[0 + 1][0], dp[0+1][0 + 1]); dp[0][0]=tri[0][0]+max(dp[0+1][0],dp[0+1][0+1]);
也即
dp[i][j]=tri[i][j]+max(dp[i+1][j],dp[i+1][j+1]);dp[i][j] = tri[i][j] + max(dp[i + 1][j], dp[i + 1][j + 1]); dp[i][j]=tri[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
這里我們把dp[i][j]dp[i][j]dp[i][j]成為狀態(tài),把上面式子稱為狀態(tài)轉移方程
根據方程,我們從最底層(dp已知)開始不斷網上的求出最底層的dp值最后得到dp[0][0]dp[0][0]dp[0][0]就是答案
其中,dp已知的部分稱為邊界

/*** 數字三角* 尋找一條從頂點到邊的路徑,使得經過的數字和最大* * triangle = {* { 7 },* { 3, 8 },* { 8, 1, 0 },* { 2, 7, 4, 4 },* { 4, 5, 2, 6, 5 }* };* * 最大* 30* * @param tri* @return*/public static int digitalTriangleDp(int[][] tri) {int[][] dp = deepCopy(tri);for (int i = tri.length - 2; i >= 0; i--) {for (int j = 0, len = tri[i].length; j < len; j++) {dp[i][j] = tri[i][j] + Math.max(dp[i + 1][j], dp[i + 1][j + 1]);}}return dp[0][0];}public static int[][] deepCopy(int[][] a) {int[][] ret = new int[a.length][];for(int i=0,l=a.length;i<l;i++)ret[i]=a[i].clone();return ret;}

顯然用遞歸也能實現上面的例子:

public static int digitalTriangle(int[][] tri) {int[][] rec = new int[tri.length - 1][tri[tri.length - 1].length - 1];for (int i = 0; i < rec.length; i++)Arrays.fill(rec[i], -1);int ans = core(rec, tri, 0, 0);Util.print(rec);System.out.println(cntOfRecCalled);return ans;}private static int core(int[][] rec, int[][] tri, int cur, int len) {if (len == tri.length - 1)return tri[len][cur];int ans = 0;if (rec[len][cur] != -1) {ans = rec[len][cur];} else {int left = core(rec, tri, cur, len + 1);int right = core(rec, tri, cur + 1, len + 1);ans = tri[len][cur] + Math.max(left, right);rec[len][cur] = ans;}return ans;}

兩者的區(qū)別在于:
遞推寫法的計算方式是自底向上(Bottom-up Approach)
而遞歸的寫法是自頂向下(Top-down Approach)

通過上面的例子我們可以發(fā)現:
如果一個問題的最優(yōu)解可以由其子問題的最優(yōu)解構造出來,則稱該問題擁有最優(yōu)子結構(Optimal Substructure)

至此我們發(fā)現,一個問題只有具有重疊子問題或最優(yōu)子結構才能用動態(tài)規(guī)劃去解決,對于沒有被選擇的子問題,由于重疊子的存在,后面可能會再考慮到它們

動態(tài)規(guī)劃與分治區(qū)別

分治分解出的子問題不一定重疊, 分治解決的問題也不一定是最優(yōu)問題

什么是貪心算法


簡單說就是: 每一步都做出一個局部最優(yōu)的選擇, 最終的結果就是全局最優(yōu)

貪心算法(Greedy Algorithm, GA)是一種特殊的動態(tài)規(guī)劃即具有局部最優(yōu)解, 且具有貪心選擇性質的

如何使用貪心算法

貪心算法的關鍵在于找到貪心策略
舉個例子

現在有無數枚,1元,5元,10元,20元,50元,100元的鈔票,請用最少的張數湊出n(n>0)元錢

分析:
假如n=176, 要湊出176元,用一些面值較大的肯定比用面值較小的用的張數少
那么貪心策略就是,每次都選面值最大但不大于n的鈔票

寫出代碼如下:

/*** 用coins湊出n* * @param n 要湊的錢* @param coins 面值* @return 每種鈔票使用的張數*/public static int[] coins(int n, int[] coins) {int[] cnt = new int[coins.length];for (int i = coins.length - 1; i >= 0; i--) {int face = coins[i];if (face <= n) {int c = n / face;n = n - c * face;cnt[i] = c;}}return cnt;}// 測試public void testCoins() {int n = 176;int[] coins = { 1, 5, 10, 20, 50, 100 };int[] coins2 = coins(n, coins);System.out.println(Arrays.toString(coins2));}

動態(tài)規(guī)劃與貪心算法區(qū)別

貪心算法采用自頂向下,但不會計算所有的子問題,而是使用貪心策略直接選擇一個子問題求解,沒有被選擇的子問題將不會被計算
而動態(tài)規(guī)劃會考慮所有的子問題

總結

以上是生活随笔為你收集整理的动态规划与贪心算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。