1. 程式人生 > >關於DP與背包

關於DP與背包

pre end ide 固定 告訴 動態規劃 進一步 滿足 基本思想

聽說過動態規劃(DP)的同學應該都知道有背包問題的存在。

首先我們來了解一下動態規劃

基本思想:

動態規劃算法通常用於求解具有某種最優性質的問題。在這類問題中, 可能會有很多可行解。沒一個解都對應於一個值,我們希望找到具有最優值的解。胎動規劃算法與分治法類似,其基本思想也是將待求解問題分解為若幹個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。與分治法不同的是,適用於動態規劃算法求解的問題,經分解得到的子問題往往不是互相獨立的。若用分治法來解這類問題,則分解得到的子問題數目太多,有些子問題被重復計算很多次。如果我們能保存已解決子問題的答案,而在需要時再找出已求得的答案,這樣就可以避免大量的重復計算,節省時間。我們可以用一個表來記錄所有已解決的子問題的答案。不管該子問題以後是否被用到,只要它被計算過,就將其結果填入表中。這就是動態規劃算法的基本思路。具體的動態規劃算法多種多樣,但它們具有相同的填表格式。

與分治法最大的差別是:適用於動態規劃求解的問題,經分解後得到的子問題往往不是互相獨立的(即下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步的求解)

應用場景:

適用於動態規劃的問題必須滿足最優化原理、無後效性和重疊性。

(1) 最優化原理(最優子結構性質):一個最優化策略具有這樣的性質,不論過去狀態和決策如何,對前面的決策所形成的狀態而言,余下的決策必須構成最優策略。簡而言之,一個最優化策略的子策略總是最優的。一個問題滿足最優化原理又稱其具有最優子結構性質。

(2) 無後效性:將各階段按照一定的次序排列好之後,對於某個給定的階段狀態,它以前各階段的狀態無法直接影響它未來的決策,而只能通過當前的這個狀態。換句話說,每個狀態都是過去歷史的一個完整總結。這就是無後向性,又稱無後效性。

(3) 子問題的重疊性:動態規劃將原來具有指數級時間復雜度的搜索算法改進成了具有多項式時間復雜度的算法。其中的關鍵在於解決冗余,這就是動態規劃算法的根本目的。動態規劃實質上是一種以空間換時間的技術,它在實現的過程中,不得不存儲產生過程中的各種狀態,所以它的空間復雜度要大於其他算法。

接下來,看背包問題,嗯,聽起來就很樸素。

其實背包問題也很簡單,無非就是在容器一定的情況下,往裏面塞東西,但是容器的容量有限,總不能把它撐爆吧,那不就弄巧成拙了,所以我們要把利益和效率最大化。在容器容量一定的情況下,根據物體的體積(質量)等條件來判斷如何裝才能使容器內所存物體的價值最大。這就用到了背包問題。

首先我們來看的是01背包問題

01背包指在容器容量一定下,每種物品只有一個,告訴你他們的體積(或者其它條件),以及價值,來使價值最大化。

代碼:(優化後為二重循環)

技術分享圖片
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int total_weight,wp_weight,every_jz;
int main() {
    cin>>total_weight;
    int dp[total_weight];
    memset(dp,0,sizeof(dp));
    cin>>wp_weight;
    int w[wp_weight],v[wp_weight];
    for(int i=1;i<=wp_weight;i++)
        cin>>w[i];
    for(int i=1;i<=wp_weight;i++)
        cin>>v[i];
    for (int i = 1; i <= wp_weight; i++)
        for (int j = total_weight; j >=w[i] ; j--)
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    cout << "總的價值為: " << dp[total_weight] << endl;
    return 0;
}
View Code

進一步的,我們再來看一下完全背包問題

完全背包指在容器容量一定下,每種物品有無數件,告訴你他們每種分別的體積(或者其它條件),價值,力求價值最大化。

代碼:(優化後為二重循環)

技術分享圖片
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int total_weight,wp_weight,every_jz;
int main() {
    cin>>total_weight;
    int dp[total_weight];
    memset(dp,0,sizeof(dp));
    cin>>wp_weight;
    int w[wp_weight],v[wp_weight];
    for(int i=1;i<=wp_weight;i++)
        cin>>w[i];
    for(int i=1;i<=wp_weight;i++)
        cin>>v[i];
    for (int i = 1; i <= wp_weight; i++)
        for (int j = w[i]; j <=total_weight ; j++)
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    cout << "總的價值為: " << dp[total_weight] << endl;
    return 0;
}
View Code

最後我們來看一下多重背包問題

即容器容量一定下,每種物品有固定件數,告訴你他們每種分別的體積(或者其它條件),價值,自然還是令價值最大化。

代碼:

技術分享圖片
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int total_weight,w_weight;
int main() {
    cin>>total_weight>>w_weight;
    int w[w_weight],v[w_weight],cot[w_weight],dp[total_weight+1];
    memset(dp,0,sizeof(dp));
    for (int i = 1; i <= w_weight; i++)
        for (int k = 1; k <= cot[i]; k++)
            for (int j = total_weight; j >= w[i]; j--)
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    cout << "總的價值為: " << dp[total_weight] << endl;
    return 0;
}
View Code

別喪氣,你現在只是單身了十幾年,以後,你還會單身好久呢。

關於DP與背包