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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

暴力递归转动态规划----以货币数问题展开

發布時間:2024/3/13 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 暴力递归转动态规划----以货币数问题展开 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

接著上道題,這道題也是用來感受暴力遞歸優化成動態規劃的套路。先想暴力嘗試的方法,然后優化成動態規劃。首先是原問題的暴力嘗試方法。只有想出嘗試方法是最難、最重要的。

我們首先來看一下題目:

給你一個數組arr,arr中所有的值都為正數且不重復,和一個整數aim。如果可以任意選擇arr中的數字,每個數字只能選一次,能不能累加得到aim,返回true或者false。

思路類似字符串的子序列問題:令f(i,sum)中的i表示數組下標,sum表示數組最開始元素到當前元素的和,以arr={3,2,7,13},aim=9為例,則遞歸過程如下圖所示:(PS:這是一張只有本人看得懂的圖,o(╯□╰)o)

所以解決步驟如下:

1.寫出嘗試(遞歸)版本

public static boolean money1(int[] arr, int aim) {return process1(arr, 0, 0, aim);}public static boolean process1(int[] arr, int i, int sum, int aim) {if (i == arr.length) {return sum == aim;}//繼續往下執行,兩種情況:要么選要么不選return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);}

2. 分析是否滿足轉動態規劃的條件:

? ? ?(1).存在大量重復計算:上述例子不夠清楚,可以換成{3,2,5,13},則到第三步將會出現兩個f(3,5),一個是由f(2,5)+0,一個是由f(2,0)+5所得,而f(3,5)的后續返回值肯定相同,所以存在重復計算。

? ? ?(2).該問題屬于“無后效性”問題:同1,不管哪條路徑到達的f(3,5),得到的返回值都一樣,則表示為“無后效性”。

綜述所述,可以轉為動態規劃。

3.?分析可變參數,哪幾個可變參數的值能代表返回狀態,幾個可變參數,以及它們的變化范圍,即可構造幾維dp表。

數組和aim不可變,i,sum可變。

4.看base case,列出不依賴的位置。(以數組{3, 2, 5 },aim=7為例,減少工作量)

二維表行代表i,列代表sum即全部元素的和(aim原則上不會超過這個數,如果超過了很顯然返回false)。

可知base case為最后一行,只有當sum==aim的時候才是T,其它都是F。所以可得下表內容:

I \ SUM012345678910
0???????????
1???????????
2???????????
3FFFFFFFTFFF

?

5.分析一個普遍位置的依賴。

return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);

可知,要知道一個普遍位置的值,就得知道它的下一行的值和下一行并向右加arr[i]列的值。例如f(2,0),根據上述代碼可知,依賴于f(3,0)和f(3,5),所以f(2,0)為F(因為f(3,0)和f(3,5)都為F)。

綜述所述,最后一行知道了,反過來就能填完了整張表。

I \ SUM012345678910
0TFTFTTFTFFF
1TFTFFTFTFFF
2FFTFFFFTFFF
3FFFFFFFTFFF

整體代碼如下:

package com.gxu.dawnlab_algorithm8;/*** 換錢問題* * @author junbin** 2019年7月12日*/ public class Money_Problem {public static boolean money1(int[] arr, int aim) {return process1(arr, 0, 0, aim);}public static boolean process1(int[] arr, int i, int sum, int aim) {if (sum == aim) { //如果遍歷過程中滿足該條件,則停止后續遍歷return true;}// sum != aimif (i == arr.length) { //如果已經到了數組末尾,則直接輸出return false;}//繼續往下執行,兩種情況:要么選要么不選return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);}//動態規劃public static boolean money2(int[] arr, int aim) {boolean[][] dp = new boolean[arr.length + 1][aim + 1];//列改為aim+1可以更節省空間for (int i = 0; i < dp.length; i++) {dp[i][aim] = true;}for (int i = arr.length - 1; i >= 0; i--) {for (int j = aim - 1; j >= 0; j--) {dp[i][j] = dp[i + 1][j];if (j + arr[i] <= aim) {dp[i][j] = dp[i][j] || dp[i + 1][j + arr[i]];}}}return dp[0][0];}public static void main(String[] args) {int[] arr = { 1, 4, 8 };int aim = 12;System.out.println(money1(arr, aim));System.out.println(money2(arr, aim));} }

總結:到現在已經體驗了兩道暴力遞歸轉動態規劃的經典題目了,可以感覺到高度的套路化,但是仍然覺得很不熟練,特別是最難的寫出嘗試版本和畫出dp表階段,繼續加油!

總結

以上是生活随笔為你收集整理的暴力递归转动态规划----以货币数问题展开的全部內容,希望文章能夠幫你解決所遇到的問題。

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