1. 程式人生 > >動態規劃 剪繩子

動態規劃 剪繩子

數組array 一半 是把 動態 題目 style 函數 new 沒有

題目:給你一根長度為n的繩子,請把繩子剪成m段(m、n都是整數,n>1並且m>1),每一段的長度記為k[0],k[1],...k[m].請問k[0]xk[1]x...xk[m]可能 的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別為2、3、3的三段,此時得到的最大乘積是18.

  我們有兩種不同的方法解決這個問題。先用常規的需要O(n^2)時間和O(n)空間的動態規劃的思路,接著用只需要O(1)時間和空間的貪婪算法來分析解決這個問題。

動態規劃

  首先定義函數f(n)為把長度為n的繩子剪成若幹段後各段長度乘積的最大值。在剪第一刀的時候,我們有n-1種可能的選擇,也就是剪出來的第一段繩子的可能長度為1,2,...n-1。因此f(n)=max(f(i)xf(n-i)),其中0<i<n.

  這是一個從上至下的遞歸公式。由於遞歸會有很多重復的子問題,從而有大量不必要的重復計算。一個更好的辦法是按照從下而上的順序計算,也就是說我們先得到f(2)、f(3),再得到f(4)、f(5),直到得到f(n)。

  當繩子的長度為2時,只可能剪成長度為1的兩段,因此f(2)等於1.當繩子的長度為3時,可能把繩子剪成長度為1和2的兩段或者長度都為1的三段,由於1x2>1x1x1,因此f(3)=2

int max(int length)
{
    if (length < 2)
    {
        return 0;
    }
    if (length == 2)
    {
        
return 1; } if (length == 3) { return 2; } int* array = new int[length + 1]; //如果length超過3,則2和3都可以直接作為一個段進行成績(不切割) array[0] = 0; array[1] = 1; array[2] = 2; array[3] = 3; int max = 0; for (int i = 4; i < length; i++) { max = 0;
for (int j = 1; j < i / 2; j++)//因為對稱所以只需要計算一半就好 { int temp = array[j] * array[i - j]; if (max < temp) { max = temp; } array[i] = max; } } max = array[length]; delete[]array; return max; }

  在上述代碼中,子問題的最優解存儲在數組array裏。數組中第i個元素表示把長度為i的繩子剪成若幹段之後各段長度乘積的最大值,即f(i)。我們註意到代碼中的第一個for循環變量i是順序遞增的,這意味著計算順序是自下而上的。因此,在求f(i)之前,對於每一個j(0<i<j)而言,f(j)都已經求解出來了,並且結果保存在array[j]裏,為了求解f(i),我們需要求出所有可能的f(j)xf(i-j)並比較得出它們的最大值。這就是代碼中第二個for循環的功能。

int max(int length)
{
    if (length < 2)
    {
        return 0;
    }
    if (length == 2)
    {
        return 1;
    }
    if (length == 3)
    {
        return 2;
    }
    //盡可能多地剪去長度為3的繩子
    int temp = length / 3;
    //當繩子最後剩下長度為4的時候,不能再剪去長度為3的繩子段
    //此時更好的方法是把繩子剪成長度為2的兩段,因此2x2>3x1
    if (length - temp * 3 == 1)
    {
        temp -= 1;
    }
    //有三種情況,最後是0,1,2,3,4(0,1,3時temp=0,pow(2,0)=1)(2,4時pow分別為2和4)
    int temp2 = (length - temp * 3) / 2;
    return (int)(pow(3, temp))*(int)(pow(2, temp2));
}

  接下來我們證明這種思路的正確性。首先,當n>=5的時候,我們可以證明2(n-2)>n並且3(n-3)>n。也就是說,當繩子剩下的長度大於或者等於5的時候,我們就把它剪成長度為3或者2的繩子段。另外,當n>=5時,3(n-3)>=2(n-2),因此我們應該盡可能地多剪長度為3的繩子段。

  前面證明的前提是n>=5。那麽當繩子的長度為4呢?在長度為4的繩子上剪一刀,有兩種可能的結果:剪成長度為1和3的兩根繩子,或者兩根長度都為2的繩子。註意到2x2>1x3,同時2x2=4也就是說,當繩子長度為4時其實沒有必要剪,只是題目的要求是至少要剪一刀。

動態規劃 剪繩子