01 背包

来源:互联网 发布:网络水军怎么收费 编辑:程序博客网 时间:2024/05/29 09:03

01背包


问题描述:给定 n 种物品和一背包,物品 i 的重量是 wi,其价值是 vi,背包容量为 c,问应如何选择装入背包中的物品,使装入背包中的物品价值最大?


回溯法

在搜索解空间树时,只要其左儿子节点是一个可行节点,搜索就进入其左子树。当右子树中可能包含最优解时才进入右子树搜索。设 r 是当前剩余物品的价值总和;cv 是当前价值;max 是当前最优价值。当 cv + r <= max 时, 可减去右子树。

void BackTrack(int cw, int cv, int i) {    if (i > n) {        max = cv;        return;    }    if (cw + w[i] <= c)        BackTrack(cw + w[i], cv + v[i], i + 1);    if (cv + r[i + 1]  >  max)        BackTrack(cw, cv, i + 1);}void Init() {    for (int i = n; i >= 1; i--)                r[i] = r[i + 1] + v[i];}

动态规划(递推法)

正常的状态定义 d(i,j) = max{d(i+1,j), d(i+1,j-W[i]) + V[i]}
d(i,j) 表示当前在第 i 层,背包剩余容量为 j 时的最大价值和,边界是 i > n 时 d(i,j) = 0;

for (int i = n; i >= 1; i--)    for (int j = 0; j <= c; j++) {        d[i][j] = (i == n ? 0 : d[i+1][j]);        if (j >= W[i])            d[i][j] = max(d[i][j], d[i+1][j-W[i]] + V[i]);    }

对称的状态定义 f(i,j) = max{f(i-1,j), f(i-1,j-W[i]) + V[i]}
f(i,j) 表示把前 i 个物品装到容量为 j 的背包中的最大价值和,边界是 i= 0 时 f(i,j) = 0;

for (int i = 1; i <= n; i++)    for (int j = 0; j <= c; j++) {        f[i][j] = (i == 1 ? 0 : f[i-1][j]);        if (j >= W[i])            f[i][j] = max(f[i][j], f[i-1][j-W[i]] + V[i]);    }

边读边计算, 不必把 V 和 W 保存下来

for (int i = 1; i <= n; i++) {    scanf("%d%d", &V, &W);    for (int j = 0; j <= c; j++) {        f[i][j] = (i == 1 ? 0 : f[i-1][j]);        if (j >= W)            f[i][j] = max(f[i][j], f[i-1][j-W] + V);    }}

滚动数组, 把数组 f 变成一维的

memset(f, 0,  sizeof(f));for (int i = 1; i <= n; i++) {    scanf("%d%d", &V, &W);    for (int j = 0; j <= c; j++)        if (j >= W)            f[j] = max(f[j], f[j-W] + V);}
0 0
原创粉丝点击