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;
}