01揹包,完全揹包,多重揹包(O(V*n))

01揹包

有n種重量和價值分別爲w[i]和v[i]的物品,每種各一個。從這些物品中挑選出總重量不超過W的物品,求所有挑選方案的價值總和最大值。

思路:容易得到該問題的狀態轉移方程爲dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
其中dp[i][j]表示從前i個物品中挑選出總重量不超過j的物品時總價值的最大值。

int w[maxn];
int v[maxn];
int n;
int dp[maxn][maxn];
int W;
void solve() {
    for (int i = 0; i<n; i++) {
        for (int j = 0; j <= W; j++) {
            if (j < w[i]) dp[i][j] == dp[i - 1][j];
            else {
                dp[i][j] = max(dp[i - 1][j - w[i]] + v[i], dp[i - 1][j]);
            }
        }
    }
    cout << dp[n][W] << endl;
}

dp方程還可以用一維數組表示:f[j]=max(f[j],f[v-w[i]]+v[i])
f[j]表示從前i個物品中挑選出總重量不超過j的物品時總價值的最大值。
需要注意的是這個方程的j應該從W到w[i]逆序遍歷

int w[maxn];
int v[maxn];
int n;
int dp[maxn][maxn];
int W;
void solve() {
    for (int i = 0; i<n; i++)
        for (int j = W; j <= w[i]; j--)
            dp[j]=max(dp[j-w[i]]+v[i], dp[j]);
    cout << dp[W] << endl;
}

完全揹包

將01揹包的每種物品數量改成無限多個,這個問題就變成了完全揹包。
容易想到狀態轉移方程爲dp[i][j]=max(dp[i-1][j-k*w[i]]]+k*v[i]) k>=0
算法複雜度太高。
我們可以將式中的k約掉,最後得到dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]]+v[i])
這個結果可以重複利用數組,方程剛好是01揹包的j的遍歷方向反過來

int w[maxn];
int v[maxn];
int n;
int dp[maxn][maxn];
int W;
void solve() {
    for (int i = 0; i<n; i++) {
        for (int j = w[i]; j <= W; j++) {
            dp[j]=max(dp[j-w[i]]+v[i], dp[j]);
            }
        }
    }
    cout << dp[W] << endl;
}

多重揹包

將01揹包中的物品數量改爲每個物品有n[i]個,問題就是多重揹包問題了。
我們容易得到多重揹包的方程dp[i][j]=max(dp[i-1] [j–k*w[i]]+k*v[i]) (0<=k<=m[i]),其中m[i]=min(n[i],j/w[i]),這個方程的複雜度不是很理想,我們可以利用j/w[i],將問題按照除出來的餘數做一個分組,過程如下:
令d=m[i],則對於當前處理的物品i,有a=j/d,b=j%d,則j=a*d+b
將上面式子帶入多重揹包dp方程,結果如下:
dp[i][j]=max(dp[i-1] [a*d+b–k*d]+k*v[i]) (0<=k<=m[i])
令s=a-k,則上式可化爲
dp[i][j]=max(dp[i-1] [b+s*d]+(a-s)*v[i]) (a-m[i]<=s<=a)
再變形,可得dp[i][j]=max(dp[i-1] [b+s*d]-s*v[i])+a*v[i]
化爲一維可得,對於每一個分組f[j]=max(f[b+s*d])+a*v[i]=max(f(b),f(b+d),f(b+2*d),……,j)
因爲每一個分組的長度都是固定的m[i],可以發現其中每一部分的最大值可以用單調隊列來完成(滑動窗口最值問題)
我們建立兩個隊列p和q,p用來判斷隊列q隊列裏是否有過期數據,q用來求最大值。(也可以看做用q隊列求p隊列中的最大值),用一維數組處理後,代碼如下:

int w[maxn];
int v[maxn];
int n[maxn];
int f[maxn];
int num;
int W;
deque<int> p;
deque<int> q;

void solve() {
    for (int i = 0; i < num; i++) {
        for (int j = 0; j < w[i]; j++){
            p.clear();
            q.clear();
            for (int k = j, a = 0; k <= W; k += w[i],a++) {
                if (p.size()==n[i]+1) { if(q.front()==p.front()) q.pop_front(); p.pop_front(); }
                int t = f[k] - a*v[i];
                p.push_back(t);
                while (!q.empty() && t >= q.back()) q.pop_back();
                q.push_back(t);
                f[k] = q.front() + a*v[i];
            }
        }
    }
    cout << f[W]<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章