背包问题整理

我的未来我决定 提交于 2020-01-18 16:06:58

 

背包问题

给定一组物品,每种物品都有自己的重量和价值,现有一个背包,能承受的重量有限,在受限制的重量下,取若干物品,使得总价值最大。这一类问题,被称为背包问题。

 

01背包(物品个数为1)

 

 

 

for (int i = 1; i <= N; ++i) {
    for (int j = 0; j <= V; ++j) {
        if(j >= c[i]) {
            dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]);
        } else {
            dp[i][j] = dp[i - 1][j];
        }
    }
}

时间上是两重循环,时间复杂度为O(NV)。空间是二维的,空间复杂度也为O(NV)。

for (int i = 1; i <= n; ++i)
    for (int j = v; j >= c[i]; --j) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }

这个做法空间复杂度也为O(V)。

 

 

多重背包(物品个数有限)

 

 

 

for (int i = 1; i <= N; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
            }
        }
    }
}

这一份代码和01背包相比,不再有else部分了,因为,k = 0的时候dp[i][j] = max(dp[i - 1][j], dp[i][j]), 相当于01背包的else部分。

 

 

 空间优化

既然多重背包的可以转换成01背包,那么我们必然也可以像01背包那样优化空间复杂度。还是按照从大到小的顺序枚举背包体积。

for (int i = 1; i <= N; i++) {
    for (int j = V; j >= 0; j--) {
        for (int k = 1; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[j] = max(dp[j - c[i] * k] + w[i] * k, dp[j]);
            }
        }
    }
}

 

 

完全背包(物品个数无限)

 

解析

虽然物品个数是无限的,但是实际上,由于背包容量有上限,每个物品最多选取的个数也是有限制的,这样可以转换成多重背包问题,进而可以转换成01背包问题。

可以用多重背包的思想来解决完全背包。

for (int i = 1; i <= N; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k * c[i] <= j; k++) {
            dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
        }
    }
}

 

 

for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= v; j++) {
        if (j >= c[i]) {
            dp[i][j] = max(dp[i][j - c[i]] + w[i], dp[i - 1][j]);
        } else {
            dp[i][j] = dp[i - 1][j];
        }
    }
}

这样我们的的算法时间复杂度O(NV),空间复杂度O(NV)。

 

不难发现,我们也可以把空间复杂度优化下来,优化成O(V)。

for (int i = 1; i <= n; i++) {
    for (int j = c[i]; j <= v; j++) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }
}

与 01 背包相比,完全背包只是第二重循环的顺序发生了翻转。

 

多重背包的二进制优化

 

 

 

 

 1 #include <iostream>
 2 using namespace std;
 3 int n[110], c[110], w[110];
 4 int nc[1000], nw[1000];
 5 int dp[5010];
 6 int main() {
 7     int N, V;
 8     cin >> N >> V;
 9     for (int i = 1; i <= N; ++i) {
10         cin >> w[i] >> c[i] >> n[i];
11     }
12     int ncnt = 0;
13     // 二进制拆分
14     for (int i = 1; i <= N; ++i) {
15         int k;
16         // 找到最大的 k
17         for (k = 1; n[i] - (1 << k) + 1 > 0; ++k) {
18             nc[ncnt] = (1 << (k - 1)) * c[i];
19             nw[ncnt] = (1 << (k - 1)) * w[i];
20             ++ncnt;
21         }
22         --k;
23         // 最后一组
24         nc[ncnt] = (n[i] - (1 << k) + 1) * c[i];
25         nw[ncnt] = (n[i] - (1 << k) + 1) * w[i];
26         ++ncnt;
27     }
28     // 01 背包
29     for (int i = 0; i < ncnt; ++i) {
30         for (int j = V; j >= nc[i]; --j) {
31             dp[j] = max(dp[j], dp[j - nc[i]] + nw[i]);
32         }
33     }
34     cout << dp[V] << endl;
35     return 0;
36 }

 

 

 

 

-

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!