动态规划4.4——背包问题
题目上添加了超链接,大家点一下题目就会自动跳转到Poj原题界面~~ 冲鸭冲鸭ヾ(◍°∇°◍)ノ゙。
前言:
建议大家按随笔顺序阅览,背包问题是一类很经典的动态规划问题,知识涵盖可以占常见动态规划类型里的10%。
由于前辈们总结的太好,现在基本上见不到单纯的模板背包问题了,命题人多会结合一些其他知识点进行综合考察,当然背包问题本身就涉及了许多不同的算法。之前有用心写过一篇关于背包问题的随笔,(点我)这里就直接给大家送上链接了。想要大家看一下(羞赧)。
动态规划组成部分:
1:确定状态
—确定最后一步(最优策略)
—抽象子问题
2:归纳转移方程
3:初始条件和边界情况
4:计算顺序
4.4.1 Charm Bracelet (3624)
题意:n个物品,每个物品有w和d属性,要求选出一定的物品,在Σw不超过m的情况下,使得Σd最大。
小笔记:01背包问题
#include <cstdio> using namespace std; #define MAX(a, b) (a > b ? a : b) int F[15000], V; //0-1背包,其中c为费用,w为价值,V为最大容量 void zeroonePack(int c, int w) { for (int v = V; v >= c; v--) F[v] = MAX(F[v], F[v - c] + w); } int main() { int n; scanf("%d%d", &n, &V); while (n--) { int w, d; scanf("%d%d", &w, &d); zeroonePack(w, d); //本题中对应的费用为w,价值为d } printf("%d\n", F[V]); return 0; }
4.4.2 Piggy-Bank (1384)
题意:n种不同的硬币,有两个属性,p代表价值,w代表重量,钱罐空时重e,满时重f,计算满时钱罐里最小可能的价值是多少。
小笔记:完全背包问题
#include <cstdio> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; int V, F[10001]; //完全背包,其中c为费用,w为价值,V为最大容量 void completePack(int c, int w) { for (int v = c; v <= V; v++) F[v] = min(F[v], F[v - c] + w); } int main() { int t; scanf("%d", &t); while (t--) { int e, f, n; scanf("%d%d%d ", &e, &f, &n); scanf(""); V = f - e; fill(F + 1, F + V + 1, INF); while (n--) { int p, w; scanf("%d%d", &p, &w); completePack(w, p); //本题中对应的费用为w,价值为p } if (F[V] == INF) printf("This is impossible.\n"); else printf("The minimum amount of money in the piggy-bank is %d.\n", F[V]); } return 0; }
4.4.3 Cash Machine (1276)
题意:有n台提款机,每台提款机提供货币的不同的面值和数量,求这些机器能提取的不超过指定限额的最大金额。
小笔记:多重背包问题
#include <cstdio> #include <algorithm> using namespace std; const int N = 100001; int V; //指定限额的最大金额 int F[N]; void zeroonePack(int c, int w) { for (int v = V; v >= c; v--) F[v] = max(F[v], F[v - c] + w); } void completePack(int c, int w) { for (int v = c; v <= V; v++) F[v] = max(F[v], F[v - c] + w); } //多重背包,其中c为费用,w为价值,m为数量 void multiplePack(int c, int w, int m) { if (c * m >= V) { completePack(c, w); return; } int k = 1; while (k < m) { zeroonePack(k * c, k * w); m -= k; k <<= 1; } zeroonePack(m * c, m * w); } int main() { int n; while (~scanf("%d%d", &V, &n)) { fill(F, F + N, 0); while (n--) { int m, d; scanf("%d%d", &m, &d); multiplePack(d, d, m); //本题中费用和价值都是d,对应数量为m } printf("%d\n", F[V]); } return 0; }
4.4.4 Coins (1742)
题意:有n种硬币,每种硬币具有不同的面值和个数,问可以组成1…m中多少个面值。
小笔记:多重背包问题
#include <cstdio> #include <algorithm> using namespace std; const int N = 100001; int V, F[N]; void zeroonePack(int c, int w) { for (int v = V; v >= c; v--) if (F[v] < F[v - c] + w) F[v] = F[v - c] + w; } void completePack(int c, int w) { for (int v = c; v <= V; v++) if (F[v] < F[v - c] + w) F[v] = F[v - c] + w; } void multiplePack(int c, int w, int m) { if (c * m >= V) { completePack(c, w); return; } int k = 1; while (k < m) { zeroonePack(k * c, k * w); m -= k; k <<= 1; } zeroonePack(m * c, m * w); } int main() { int n; while (scanf("%d%d", &n, &V) && (n || V)) { fill(F, F + N, 0); F[0] = 1; int a[101], c[101]; for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &c[i]); for (int i = 0; i < n; i++) multiplePack(a[i], 0, c[i]); //本题费用对应a,数量对应c,因为只涉及面值,不涉及每种硬币的价值,所以不考虑w int ans = 0; for (int i = 1; i <= V; i++) if (F[i]) ans++; printf("%d\n", ans); } return 0; }