1. 程式人生 > 其它 >每日小記-動態規劃:怎麼使用迭代公式構造迴圈次序:以最優除法為例

每日小記-動態規劃:怎麼使用迭代公式構造迴圈次序:以最優除法為例

記錄了啥

動態規劃中
在拿到迭代公式之後(也就是知道了如何利用已知部分的資訊求未知部分的資訊之後)
怎麼寫迴圈(怎麼迴圈,好在裡面寫迭代公式)

題目

最優除法https://leetcode-cn.com/problems/optimal-division/
簡單說:一個數組全是正整數,從第一個到最後一個挨個除÷,也就是a/b/c/d/e....、y/z
求:在裡面新增括號來改變運算次序,求出最大結果

這道題有最腦殘的解法(媽的答案是固定,把第二個到最後一個括起來就行,我說我怎麼效率那麼低,3層迴圈的動態規劃效率能高就tm見鬼了)

但是用來鍛鍊一下動態規劃還是可以的
簡單說,
要構造一個三維(lenlen4)的儲存空間來存放輔助陣列
其中ij位置記錄從i到j的數能形成的最大值和最小值
這樣,根據(n,m)和(m+1,p)的資訊就能知道(n,p)的資訊

//最初我考慮的遍歷方式就是三層,外層i,中層j,內層列舉k,其中:i<k<j
//但是這樣的結果就是,求ij的時候,(k+1,j)還沒有求到

//答案採用的方式是,i為起步索引,k作為分割索引,c為長度,用i+c作為結尾索引
//這樣做的功能是啥呢,就是最外層控制每個段的長度,先處理長度為2的段,再3....
//再找長度為x的段的最大最小值時,任何一個分割位置產生的長度小於x的段資訊都是知道的

//而初始長度為1的段都在初始化完成了
//中層i決定起始位置,k列舉分割位置

總結

動態規劃的過程就是通過輔助空間記錄之前求解過的過程,避免二次計算,
那麼必須注意,如何安排迴圈方式使得在做某一次計算的時候,所需要的中間結果都是已經計算好的
本題我最初的考慮方式中,每一步求解時,都只知道了左段,不知道右段資訊
因為我最外側控制大的執行方向的變數是起始位置
而答案使用的是求解段的長度,這樣長度從2開始增加,求解n長的時候,拆分的任何i,n-i兩個段的資訊都是求過的,已知的

直接貼程式碼吧

點選檢視程式碼
public static String optimalDivision(int[] nums) {
    int len=nums.length;
    //後期為了字串拼接方便啥的,可以考慮使用Stingbuffer
    String[][][] maxMinString=new String[2][len][len];//最大值最小值對應的字串
    double[][][] maxMin=new double[2][len][len];//記錄ij區間的公式的最大值和最小值
    for (int i = 0; i < len; i++) {
        for (int j = 0; j < len; j++) {
            if(i==j){
                maxMin[0][i][j]=nums[i];
                maxMin[1][i][j]=nums[i];
                maxMinString[0][i][j]=String.valueOf(nums[i]);
                maxMinString[1][i][j]=String.valueOf(nums[i]);
            }else{
                maxMin[0][i][j]=0;
                maxMin[1][i][j]=10000;
            }

        }
    }

    //最初我考慮的遍歷方式就是三層,外層i,中層j,內層列舉k,其中:i<k<j
    //但是這樣的結果就是,求ij的時候,(k+1,j)還沒有求到

    //答案採用的方式是,i為起步索引,k作為分割索引,c為長度,用i+c作為結尾索引
    //這樣做的功能是啥呢,就是最外層控制每個段的長度,先處理長度為2的段,再3....再找長度為x的段的最大最小值時,任何一個分割位置產生的長度小於x的段資訊都是知道的
    //而初始長度為1的段都在初始化完成了
    //中層i決定起始位置,k列舉分割位置
    for (int c = 1; c < len; c++) {//至少要間隔1個,也就是長度為2
        for (int i = 0; i+c <len; i++) {
            for (int k = i; k < i+c; k++) {//k作為了一個劃分位置,應該是代表著兩個索引之間,所以這裡使用索引k和k+1代表兩側的分割位置
                //這裡是迭代公式,對於分割後的兩段資訊,如果切割後的兩段相除的結果可以更新整段的資訊,則更新
                //更新最小值,左端最小值/右端最大值
                if(maxMin[1][i][i+c]>maxMin[1][i][k]/maxMin[0][k+1][i+c]){
                    maxMin[1][i][i+c]=maxMin[1][i][k]/maxMin[0][k+1][i+c];
                    //更新字串記錄,和括號,除號
                    //首先,兩端的最值的字串長成啥樣都記錄下來了,那麼整體的最一下拼接就行了,但是要注意,右側長度大於1時要套括號
                    if((i+c)!=(k+1)){
                        maxMinString[1][i][i+c]=maxMinString[1][i][k]+"/("+maxMinString[0][k+1][i+c]+")";
                    }
                    else{
                        maxMinString[1][i][i+c]=maxMinString[1][i][k]+"/"+maxMinString[0][k+1][i+c];
                    }
                }
                //更新最大值,左端最大值/右端最小值
                if(maxMin[0][i][i+c]<maxMin[0][i][k]/maxMin[1][k+1][i+c]){
                    maxMin[0][i][i+c]=maxMin[0][i][k]/maxMin[1][k+1][i+c];
                    //更新字串記錄,和括號,除號
                    if((i+c)!=(k+1)){
                        maxMinString[0][i][i+c]=maxMinString[0][i][k]+"/("+maxMinString[1][k+1][i+c]+")";
                    }
                    else{
                        maxMinString[0][i][i+c]=maxMinString[0][i][k]+"/"+maxMinString[1][k+1][i+c];
                    }
                }
            }
        }
    }
    return maxMinString[0][0][len-1];
}

本文來自部落格園,作者:熒惑微光,轉載請註明原文連結:https://www.cnblogs.com/yinghuoweiguang/p/15942724.html