leetcode:剪繩子

題目來源:力扣

題目介紹

給你一根長度爲 n 的繩子,請把繩子剪成整數長度的 m 段(m、n都是整數,n>1並且m>1),每段繩子的長度記爲 k[0],k[1]…k[m] 。請問 k[0]k[1]…*k[m] 可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到的最大乘積是18。
========================================================
示例 1:
輸入: 2
輸出: 1
解釋: 2 = 1 + 1, 1 × 1 = 1
示例 2:
輸入: 10
輸出: 36
解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 58
=========================================================

審題:

對於這樣一道最優化問題,我們考慮使用動態規劃方法解決.
我們使用兩個狀態變量,分別爲當前繩子長度,以及裁剪段數.我們使用二維數組S[i][j]表示長度爲i的繩子剪裁爲j段時,每段長度乘積的最大值.對於每一個子問題,例如求解狀態(i, j)下的最優值,我們有多少種選擇?我們首先需要選擇的是一個切分點,將當前繩子切分爲左右兩段,其後我們需要確定左側需要切分爲多少段.如果我們選擇將繩子切分爲長度爲k和i-k的兩段,並且左側切分r段,則當前選擇下的長度爲i的繩子切分爲j段的最大乘積等於S[k][r]*S[i-k][j-r].我們的選擇規模爲O(N2)O(N^2), 判斷每一種選擇,我們可以做出最優選擇.

class Solution {
    public int cuttingRope(int n) {
        int[][] S = new int[n+1][n+1];
        for(int i = 1; i <= n; i++)
            S[i][1] = i;

        for(int m = 2; m <= n; m++){//裁剪m段
            for(int l = m; l <=n; l++){//長度爲l
                S[l][m] = Integer.MIN_VALUE;
                for(int left = 1; left < l; left++){//裁剪後,左側的長度
                    //左側裁剪r段, 右側裁剪m-r段
                    for(int r = 1; r < m; r++){
                        S[l][m] = Math.max(S[l][m], S[left][r] * S[l-left][m-r]);
                    }
                }
            }
        }

        int max = S[n][2];
        for(int i = 2; i <= n; i++)
            max = Math.max(max, S[n][i]);
        return max;
    }
}

複雜度分析

不難發現,上述算法的時間複雜度爲O(N4)O(N^4), 空間複雜度爲O(N2)O(N^2).

說實話,上面我的設計一點也不優雅,而且算法時間複雜度很高.沒辦法,簡潔的辦法似乎總是很難第一時間想到.藍瘦香菇.

我們可以僅維護一個狀態變量即當前繩子長度,我們使用S[i]表示長度爲i是的最優切分方案下最大乘積.在每一步,我們選擇不會被繼續分割的位置對繩子進行切分.而進行切分後,我們可以終止切分.因爲我們已經將繩子分成了兩段,符合題目要求,也可以進一步選擇將右段繼續切分.
對於長度爲i的繩子,我們我們在k處進行切割,則如果不繼續進行切割,當前乘積爲k * (i-k), 如果我們繼續進行切分,則乘積爲k * S[i-k], 我們可以選擇最優方案.

class Solution {
    public int cuttingRope(int n) {
        int[] S = new int[n+1]; 
        //S[i]表示長度爲i最佳方案
        S[1] = 1;
        S[2] = 1;
        for(int i = 3; i <= n; i++){
            //對於每一步,我們可以選擇切分2段或更多段
            int max = Integer.MIN_VALUE;
            for(int j = 1; j < i; j++){
                max = Math.max(max, j * S[i-j]); //繼續切分
                max = Math.max(max, j * (i-j)); //不切分
            }
            S[i] = max;
        }
        return S[n];

    }
}

複雜度分析

上述算法的時間複雜度爲O(N2)O(N^2),空間複雜度爲O(N)O(N).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章