背包问题——动态规划

来源:互联网 发布:怎么在淘宝上开S音速店 编辑:程序博客网 时间:2024/06/06 04:07

背包问题——动态规划

一、单副本背包(经典的0/1背包问题)
问题描述:

单副本背包问题:
有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值为w[i]
求解将哪些物品装入背包可使价值总和最大。

分析:

用f[i][v]表示前i件物品放入一个容量为v的背包可以获得的最大价值,如果我们能计算出所有f[i][v]的值,那么f[N][V]就是答案

用动态规划算法解决问题的主要思路就是将原问题转化为规模更小的子问题.
因此我们假设对于只有i-1件物品的情况下,我们已经求得背包容量为0到V时的最优值,也就是说所有的f[i-1][v]的值都已求出
我们再来考虑有i件物品的情况,如果第i件物品不放进容量为v的背包,则f[i][v] = f[i-1][v].
如果第i件物品放进容量为v的背包,则f[i][v] = f[i-1][v-c[i]] + w[i]. 这种情况只有当v>=c[i]时才有可能.
最后,我们还需要再考虑一下动态规划的边界条件,当i=0,也就是没有任何物品的情况下,显然对于任意的容量v,f[0][v]=0
综上,我们可以得到动态规划的状态转移方程:
这里写图片描述

解法一:
这里写图片描述
代码如下:

/*单副本背包问题:有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值为w[i]求解将哪些物品装入背包可使价值总和最大。 */#include <iostream>using namespace std;int max(int a, int b) {    return a > b ? a : b;}// (空间复杂度为O(NV)int KP(int N, int V, int c[], int w[]) {    int f[N + 1][V + 1];    for (int v = 0; v <= V; v++) {        f[0][v] = 0;    }    for (int i = 1; i <= N; i++) {        for (int v = 0; v <= V; v++) {            if (v < c[i]) f[i][v] = f[i - 1][v];            else f[i][v] = max(f[i - 1][v], f[i - 1][v - c[i]] + w[i]);        }    }    for (int i = 0; i <= N; i++) {        for (int v = 0; v <= V; v++) {            cout << f[i][v] << " ";        }        cout << endl;    }    return f[N][V];}int main() {    int N, V;    cin >> N >> V;    int c[N + 1], w[N + 1];    for (int i = 1; i <= N; i++) {        cin >> c[i] >> w[i];    }    cout << KP(N, V, c, w) << endl;    return 0;}

程序运行结果如下:(第一行为物品件数N和容量V,接下来N行每一行为费用c[i]和价值w[i])
这里写图片描述

解法二:
这里写图片描述
代码如下:

/*单副本背包问题:有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值为w[i]求解将哪些物品装入背包可使价值总和最大。 */#include <iostream>using namespace std;int max(int a, int b) {    return a > b ? a : b;}// (空间复杂度为O(V)int KP(int N, int V, int c[], int w[]) {    int f[V + 1];    for (int v = 0; v <= V; v++) {        f[v] = 0;    }    for (int i = 1; i <= N; i++) {        for (int v = V; v >= c[i]; v--) {            f[v] = max(f[v], f[v - c[i]] + w[i]);        }    }    for (int v = 0; v <= V; v++) {        cout << f[v] << " ";    }    cout << endl;    return f[V];}int main() {    int N, V;    cin >> N >> V;    int c[N + 1], w[N + 1];    for (int i = 1; i <= N; i++) {        cin >> c[i] >> w[i];    }    cout << KP(N, V, c, w) << endl;    return 0;}

程序运行结果如下:(第一行为物品件数N和容量V,接下来N行每一行为费用c[i]和价值w[i])
这里写图片描述


二、可重复背包(多副本背包)
问题描述:

可重复背包(多副本背包):
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],,价值是w[i]
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

分析:

用f[v]表示N种物品放入一个容量为v的背包可以获得的最大价值,其状态转移方程便是:
f[v]=max{f[v-c[i]]+w[i] | v>=c[i], i=1, 2, …, N}

伪代码如下:
这里写图片描述

代码如下:

#include <iostream>using namespace std;int max(int a, int b) {    return a > b ? a : b;}// (空间复杂度为O(V)int KP(int N, int V, int c[], int w[]) {    int f[V + 1];    for (int v = 0; v <= V; v++) {        f[v] = 0;    }    for (int v = 1; v <= V; v++) {        for (int i = 1; i <= N; i++) {            if (v >= c[i]) {                f[v] = max(f[v], f[v - c[i]] + w[i]);            }        }    }    for (int v = 0; v <= V; v++) {        cout << f[v] << " ";    }    cout << endl;    return f[V];}int main() {    int N, V;    cin >> N >> V;    int c[N + 1], w[N + 1];    for (int i = 1; i <= N; i++) {        cin >> c[i] >> w[i];    }    cout << KP(N, V, c, w) << endl;    return 0;}

程序运行结果如下:(第一行为物品件数N和容量V,接下来N行每一行为费用c[i]和价值w[i])
这里写图片描述


三、二维背包
问题描述:

对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;
对于每种代价都有一个可付出的最大值(背包容量)。
设第i件物品所需的两种代价分别为a[i]和b[i].
两种代价可付出的最大值(两种背包容量)分别为V和U物品的价值为w[i]
问:怎样选择物品可以得到最大的价值?

分析:

费用加了一维,只需状态也加一维即可. 设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值. 状态转移方程就是:
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

代码如下:

#include <iostream>using namespace std;int max(int a, int b) {    return a > b ? a : b;}int KP(int N, int V, int U, int a[], int b[], int w[]) {    int f[V + 1][U + 1];    for (int v = 0; v <= V; v++) {        for (int u = 0; u <= U; u++) {            f[v][u] = 0;        }    }    for (int i = 1; i <= N; i++) {        for (int v = V; v >= a[i]; v--) {            for (int u = U; u >= b[i]; u--) {                f[v][u] = max(f[v][u], f[v - a[i]][u - b[i]] + w[i]);            }        }    }    for (int v = 0; v <= V; v++) {        for (int u = 0; u <= U; u++) {            cout << f[v][u] << " ";        }        cout << endl;    }    cout << endl;    return f[V][U];}int main() {    int N, V, U;    cin >> N >> V >> U;    int a[N + 1], b[N + 1], w[N + 1];    for (int i = 1; i <= N; i++) {        cin >> a[i] >> b[i] >> w[i];    }    cout << KP(N, V, U, a, b, w) << endl;    return 0;}

程序运行结果如下:(第一行为物品件数N和容量V和容量U,接下来N行每一行为费用a[i]、费用b[i]和价值w[i])
这里写图片描述


四、分组背包
问题描述:

有N件物品和一个容量为V的背包. 第i件物品的费用是c[i],价值是w[i]. 这些物品被划分为若干组,每组中的物品互相冲突,最多选一件.
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

分析:

这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}

代码如下:

#include <iostream>using namespace std;const int V = 1000;const int T = 3;const int K = 2;int w[K][T] = {{5, 10, 8}, {15, 20, 15}};// 表示每一种物品的价值int c[K][T] = {{200, 300, 400}, {400, 800, 200}};// 表示每一种物品的体积int f[V + 1];int max(int a, int b) {    return a > b ? a : b;}int KP() {    for (int i = 0; i <= V; i++) {        f[i] = 0;    }    for (int k = 0; k < K; k++) {        for (int v = V; v >= 0; v--) {// 将每一个分组当做一次01背包,故计算顺序为V递减            for (int i = 0; i < T; i++) {// 针对每一个分组中的第一个i                if (v - c[k][i] >= 0) {                    f[v] = max(f[v], f[v - c[k][i]] + w[k][i]);                }            }        }    }    return f[V];}int main() {    cout << KP() << endl;    return 0;}

程序运行结果如下:
这里写图片描述

原创粉丝点击