题目上添加了超链接,大家点一下题目就会自动跳转到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
", 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.
");
else
printf("The minimum amount of money in the piggy-bank is %d.
", 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
", 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
", ans);
}
return 0;
}