部分背包,01背包,完全背包,多重背包
部分背包
【部分背包】问题描述:n件物品,第i件物品价值 vi 元,重wi 磅。希望用 W磅的背包 拿走最重的物品。第i件物品可以都拿走,也可以拿走一部分。(物品可以分割所以称为部分背包)
因为每一个物品都可以分割成单位块,单位块的利益越大显然总收益越大,所以它局部最优满足全局最优,可以用贪心法解答。
(1)先将单位块收益按从大到小进行排序;
(2)初始化背包的剩余体积和当前价值;
(3)从前到后考虑所有物品:a.如果可以完全放入,当前价值加上物品总价值,剩余体积减去物品总体积;b.如果可以部分放进,当前价值加上物品价值*剩余体积,使剩余体积为0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <bits/stdc++.h> using namespace std;
const int MAXN = 1e4; struct node { double w, v; }stru[MAXN]; int dp[MAXN] = {0}; double arr[MAXN] = {0}; bool cmp(node x, node y) { return x.v/x.w > y.v/y.w; } int main() { int n, d; cin >> n >> d; for(int i = 0; i < n; i++) cin >> stru[i].w; for(int i = 0; i < n; i++) cin >> stru[i].v; sort(stru, stru + n, cmp);
int i, j; for(i = 0; i < n; i++) { if(stru[i].w > d) break; d -= stru[i].w; arr[i] = 1.0; } if(i < n) arr[i] = d / stru[i].w; double ans = 0; for(j = i; j >= 0; j--) ans += stru[j].v*arr[j]; printf("%0.2lf", ans);
return 0; }
|
01背包
入门
动态规划专题 01背包问题详解【转】 - fancy_boy - 博客园
二维背包
一维背包
初始化细节
我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。
如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0
为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。
题目
模板题-HDU2602-Bone Collector
HDU2602
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream> #include <algorithm> using namespace std; const int len = 10000;
int main() { ios::sync_with_stdio(false); int t; cin >> t; while(t--) { int n, m; cin >> n >> m; int dp[len] = {0}, val[len] = {0}, w[len] = {0}; for(int i = 1; i <= n; i++) cin >> val[i]; for(int i = 1; i <= n; i++) cin >> w[i]; for(int i = 0; i <= n; i++) for(int j = m; j >=w[i]; j--) { dp[j] = max(dp[j], dp[j - w[i]] + val[i]); } cout << dp[m] << endl; } return 0; }
|
01变形-HDU2546-饭卡
hdu2546
比较详细的解析:动态规划专题 01背包问题详解 HDU 2546 饭卡 - fancy_boy - 博客园
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream> #include <algorithm> using namespace std; const int len = 1005;
int main() { int n, m; while(cin >> n&& n != 0) { int w[len] = {0}; int dp[len] = {0}; for(int i = 1; i <= n; i++) cin >> w[i]; int m; cin >> m; if(m <= 4) cout << m << endl; else { sort(w + 1, w + n + 1); for(int i = 1; i <= n - 1; i++) for(int j = m - 5; j >= 0; j--) { if(j >= w[i]) { dp[j]= max(dp[j], dp[j - w[i]] + w[i]); } } int s = 0; for(int j = 1; j <= m-5; j++) { if(s<dp[j]) s=dp[j]; } cout << m - s - w[n] << endl; } } return 0; }
|
01变形-nyoj860-用价值保存质量
nyoj860 又见01背包
hdu2639(01背包求第k优解)
题目:HDU2639-Bone Collector II
题解:csdn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <iostream> #include <cstring> using namespace std;
const int len = 1000; int val[len], cost[len], dp[len][len];
int main() { ios::sync_with_stdio(false); int t; cin >> t; while(t--) { int n, v, k; cin >> n >> v >> k; for(int i = 1; i <= n; i++) cin >> val[i]; for(int i = 1; i <= n; i++) cin >> cost[i]; memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) for(int j = v; j >= cost[i]; j--) { int A[len], B[len]; for(int kk = 1; kk <= k; kk++) { A[kk] = dp[j - cost[i]][kk] + val[i]; B[kk] = dp[j][kk]; } int a = 1, b = 1, c = 1; A[k + 1] = -1, B[k + 1] = -1; while(c <= k && (A[a] != -1 || B[b] != -1)) { if(A[a] > B[b]) dp[j][c] = A[a++]; else dp[j][c] = B[b++]; if(dp[j][c] != dp[j][c - 1]) c++; } } cout << dp[v][k] << endl; } return 0; }
|
限制条件的01背包
hdu5188
01变形-洛谷P1802
参考题解: 半仙胡小桃
一个变形版的01背包。
dp[i]表示用i瓶药获得的最多经验。
决策?
当i>=use时,可以选择打败或者不打败
dp[i]=max(dp[i]+lose,dp[i-use]+win)。
当i<use时,无法战胜对方。
dp[i]+=lose
至于数据范围,最后输出时强制转换一下就行了。(这里需要long long)
题目:P1802
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <algorithm> using namespace std; const int len = 100000; typedef long long ll; ll dp[len]; ll win[len],lose[len],use[len]; int main() { int n,m; cin >> n >> m; for(int i = 1; i <= n; i++) cin >> lose[i] >> win[i] >> use[i]; for(int i = 1; i <= n; i++) { for(int j = m; j >= use[i]; j--) dp[j] = max(dp[j] + lose[i], dp[j - use[i]] + win[i]); for(int j = use[i] - 1;j >= 0; j--) dp[j] += lose[i]; } cout << 5 * dp[m]; return 0; }
|
P1757 通天之分组背包
题目:p1757
这一题是分组背包问题。思路是把每一组看做一个物品,转化为01背包做。
对于分组背包,可以这样想:虽然分成很多组,但只能选一个,或者不选,这和01背包是一样的,也就是说,对于01背包里每一个独一无二的物品,对应的分组背包就是每一组中选择一个物品,这样来看,完全就是01背包问题。
可以想一下01背包的状态方程,和这个外两层的循环是一样的,不一样的是里面又加了一层,这层循环是遍历每一组的物品用的,对于dp[]中每一个状态都是循环了一遍每一组的物品才换到下一个的,所以对后面的没有影响,也就保证了每组物品最多只有一件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream> #include <algorithm> using namespace std;
int w[1100] = {0}; int v[1100] = {0}; int num[1100][1100] = {0}; int dp[1100]; int main() { int m ,n, g; cin >> m >> n; int Maxg = 0; for(int i = 1; i <= n; i++) { cin >> w[i] >> v[i] >> g; Maxg = max(Maxg, g); num[g][++num[g][0]] = i; } for(int i = 1; i <= Maxg; i++) for(int j = m; j >= 0; j--) for(int k = 1; k <= num[i][0]; k++) if(j >= w[num[i][k]]) dp[j] = max(dp[j], dp[j - w[num[i][k]]] + v[num[i][k]]); cout << dp[m]; return 0; }
|
HDU2955
HDU3466
HDU1881
完全背包
动态规划之完全背包详解
大意:给出存钱罐本身的重量和装钱后的重量,以及存钱罐中钱的面值和重量,求存钱罐装满时,钱的总和最小是多少
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
#include <iostream> #include <algorithm> using namespace std; const int INF = 1E9; const int MAXN = 1E5 + 100; int dp[MAXN]; int weight[MAXN], value[MAXN];
void init() { for(int i = 1; i < MAXN; i++) dp[i] = INF; }
int main() { ios::sync_with_stdio(false); int t; cin >> t; while(t--) { int empty, full; cin >> empty >> full; int V = full -empty; int n; cin >> n; init(); for(int i = 1; i <= n; i++) cin >> value[i] >> weight[i]; for(int i = 1; i <= n; i++) for(int j = weight[i]; j <= V; j++) dp[j] = min(dp[j - weight[i]] + value[i], dp[j]); if(dp[V] == INF) cout << "This is impossible." << endl; else cout << "The minimum amount of money in the piggy-bank is " << dp[V] << "." << endl; } return 0; }
|
多重背包
HDU2191-悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
此题当中是把重量当成每件物品的价值。把总的钱数当成背包的容量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
#include <iostream> #include <cstring> #include <algorithm>
using namespace std;
const int MAXN = 1e2 + 10; int cntValue, cntKind, value[MAXN], weight[MAXN], bag[MAXN], dp[MAXN];
int main() { ios::sync_with_stdio(false); int t; cin >> t; while(t--) { memset(dp, 0, sizeof(dp)); cin >> cntValue >> cntKind; for(int i = 0; i < cntKind; i++) cin >> value[i] >> weight[i] >> bag[i]; for(int i = 0; i < cntKind; i++) for(int j = 0; j < bag[i]; j++) for(int k = cntValue; k >=value[i]; k--) dp[k] = max(dp[k], dp[k - value[i]] + weight[i]); cout << dp[cntValue] << endl; } return 0; }
|