動態規劃常見問題

1.找零問題
問題描述:目前人民幣面值有1元、5元、10元、20元、50元、100元,假設現在需要給顧客找零n元,有多少種面值組合方式?

問題思路:設f[n][j]表示使用前j種面值組合成n元的組合方式種類數(每種面值可能用到,也可能不用),第j種面值爲v[j],那麼f[n][j] = f[n][j - 1] + f[n - v[j]][j],其中f[n][j - 1]表示不用第j種面值組合成n元的組合方式種類數,而f[n - v[j]][j]理解成,首先使用一張第j種面值,則剩餘n-v[j]元,可以繼續使用第j種組合也可以不用。特別的,有f[0][j] = 1。

C++:

#include <iostream>
using namespace std;
int v[] = {1,5,10,20,50,100};
int f(int n ,int j){
    if(n == 0)     return 1;
    if(n < 0 || j < 0)  return 0;
    return f(n ,j - 1) + f(n - v[j], j);
}
int main(){
    int n;
    cin >> n;
    cout << f(n,6 - 1) << endl;
    return 0;
}

測試用例:
輸入
100
輸出
344

2.鋼條切割問題
問題描述:一家公司購買長鋼條,將其切割成短鋼條出售,切割本身沒有成本,長度爲i的短鋼條的價格爲Pi。那給定一段長度爲n的鋼條和一個價格表Pi,求鋼條的切割方案使得收益Rn最大。如一個Pi如下:
這裏寫圖片描述
求長度爲n的鋼條所有切割方法的最大收益?

問題思路:把長度爲n的鋼條看做n段長度爲1的鋼條,從鋼條的最左端開始的相鄰連接點開始,可以選擇切割或者不切割,故總共的切割方案有2的n-1次方(這是在假設每一小段鋼條不一樣的情況下),實際總的切割方案數可以利用上述的找零思想。現在問題是所有切割方法的最大收益,假設長度爲n的鋼條的所有切割方案中最大收益爲f[n],則f[n] = max{Pi[n] ,f[1] + f[n - 1],f[2] + f[n - 2],…}。

C++:

#include <iostream>
using namespace std;
void max_cut(int f[],int Pi[],int n){
    for(int i = 1;i <= n; ++i ){
        int profit = Pi[i]; //初始化長度爲i的鋼條切割最大收益爲不進行切割的價值
        for(int j = 1;j <= i/2; ++j){
            profit = profit > (f[j] + f[i - j]) ? profit : (f[j] + f[i - j]);
        }
        f[i] = profit;
    }
}
int main(){
    int n,k;
    cin >> n;
    int *Pi = new int[n + 1]();
    int *f = new int[n + 1](); //必須初始化爲0
    for(k = 1;k <= n; ++k){
        cin >> Pi[k];
    }
    max_cut(f,Pi,n);
    cout << f[n] << endl;
    return 0;
}

測試用例:
輸入
10
1 5 8 9 10 17 17 20 24 30
輸出
30

3.01揹包問題
問題描述:一個揹包體積爲V,有n件物品,對每件物品i,其體積爲c[i],價值爲w[i],求揹包能容納的最大價值。

問題思路:假設f[v][i]表示體積爲v的書包使用前i種(可以用也可以不用)容納的最大價值,則f[v][i] = max{f[v][i - 1],f[v - c[i]][i - 1] + w[i]}。

C++:

#include <iostream>
using namespace std;
void package01(int f[],int c[],int w[],int n,int V){
    for(int i = 1:i <= n; ++i){
        for(int v = V;v >= c[i]; --v){
            f[v] = f[v] > (f[v - c[i]] + w[i]) ? f[v] : (f[v - c[i]] + w[i]);
        }
    }
}
int main(){
    int n,V,k;
    cin >> n >> V;
    int *f = new int[V + 1](); //必須初始化爲0
    int *c = new int[n + 1]();
    int *w = new int[n + 1]();
    for(k = 1;k <= n; ++k){
        cin >> c[k];
    }
    for(k = 1;k <= n; ++k){
        cin >> w[k];
    }
    package01(f,c,w,n,V);
    cout << f[V] <<endl;
    return 0;
}

測試用例:
輸入
4 10
2 3 5 6
3 4 7 8
輸出
14

問題描述:輸入一個整型數組,數組裏有正數也有負數。數組中連續的一個或多個整數組成一個子數組,每個子數組都有一個和。求所有子數組的和的最大值。要求時間複雜度爲O(n)。

int max_array(int A[],int n){
    int max = 0,temp = 0;
    for(int i = 0;i < n; ++i){
        if(temp <= 0)   temp = A[i];
        else    temp += A[i];
        if(temp > max)  max = temp; 
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章