1. 程式人生 > >[演算法學習筆記]動態規劃之鋼條切割問題

[演算法學習筆記]動態規劃之鋼條切割問題

問題描述

有一個長度為n的鋼條需要切割成短鋼條出售,長度不同的鋼條售價也不同,如下:

長度i 1 2 3 4 5 6 7 8 9 10
價格p[i] 1 5 8 9 10 17 17 20 24 30

那麼怎麼切割才能獲得最大利益呢

暴力解決思路

只要列出每種切割方案,然後比較一下哪種切割方案利潤最大不就可以了嗎(手動滑稽
假設有一個長度為4的鋼條,那麼有一下四中切割方案

編號 切割方案 利潤
■■■■ 9
■■■,■ 9
■■,■■ 10
■■,■,■ 6
■,■,■,■ 4

發現當鋼條長度為4時,切割成兩個長度為2的短鋼條利潤最大。而且當切割鋼條時,若切割完成後的鋼條長度小於等於1的話,則可以把這個鋼條看成規模更小的相同問題,用同樣的方法先求出這個短鋼條的最大利潤,然後比較所有組合的利潤,構成原問題的解。
鋼條切割問題滿足最優子結構的性質:問題的最優解由相關問題的最優解組合而成,而這些子問題可以獨立求解。
通過遞迴的求解方式可以寫出簡單的求解程式:從鋼條的左邊切割下一段長度為i的短鋼條,然後右邊為長度就為n-i的短鋼條,然後對右邊繼續遞迴求解,知道右邊的長度為0,則返回零。

#include <stdio.h>
#define max(a,b) (a>b?a:b)
#define INF 0x7fffff; 

// 不同長度鋼條的價格 
int p[11] = {
    0,1,5,8,9,10,17,17,20,24,30
};

int cutROd(int p[], int n){
    int i, q;
    if(n == 0)
        return 0;
    q = -INF;
    for(i = 1; i <= n; i++){
        q = max(q, p[i] + cutROd(p, n - i));
    }

    return
q; } int main(){ int n; scanf("%d", &n); printf("%d\n", cutROd(p, n)); return 0; }

指數爆炸

圖樣圖森破啊,這個程式的複雜度是2^n,所以輸入大一點的數就會指數爆炸了,等半天結果才會出來,所以這樣寫太native了

更高效的方法

改進上面的程式

上面的程式之所以低效,因為有很多重複的計算啊,比如說計算長度為4的鋼條,便有多次計算了長度為2和3的最優解,更不要說在大一點的數要重複計算多少次了。但是,如果能避免這些重複計算的,演算法的效率就會非常高了,只要在第一次計算中吧結果記錄下來,下次需要用到的時候直接呼叫就可以了。

#include <stdio.h>
#define max(a,b) (a>b?a:b)
#define INF 0x7fffff; 

int p[11] = {
    0,1,5,8,9,10,17,17,20,24,30
};

int memoizedCutRodAux(int p[], int n, int r[]){
    int q;
    if(r[n] >= 0)
        return r[n];
    if(n == 0)
        q = 0;
    else{
        q = -INF;
        int i;
        for(i = 1; i <= n; i++)
            q = max(q, p[i] + memoizedCutRodAux(p, n-i, r)) ;
    }
    r[n] = q;
    return q;
}

int memoizedCutRod(int p[], int n){
    int r[11], i;
    for(i = 0; i < 11; i++)
        r[i] = -INF;
    return memoizedCutRodAux(p, n, r);
}

int main(){
    int n;
    scanf("%d", &n);
    printf("%d\n", memoizedCutRod(p, n));

    return 0;
} 

另一種解法

#include <stdio.h>
#define max(a,b) (a>b?a:b)
#define INF 0x7fffff; 

int p[11] = {
    0,1,5,8,9,10,17,17,20,24,30
};

int buttomUpCutRod(int p[], int n){
    int r[n+1];
    r[0] = 0;
    int i, j;
    for(i = 1; i <= n; i++){
        int q = -INF;
        for(j = 1; j <= i; j++)
            q = max(q, p[j] + r[i - j]);
        r[i] = q;
    }

    return r[n];
} 

int main(){
    int n;
    scanf("%d", &n);
    printf("%d\n", buttomUpCutRod(p, n));

    return 0;
}