测试案例:http://poj.org/problem?id=3624
需求:0-1背包问题(下面代码在poj上不是超时就是内存消耗过大,毕竟算法的时间复杂度是O(nW))
xj =0 或1的背包问题
输入格式:
行数 内容
1: N M (种类,最大重量)
2~1+N: W_i D_i (单一重量,价值)
状态转移方程:f(i,j) = max(f(i,j),f(i-1,j-ui)+wi)
例如:
输入:
4 6
1 4
2 6
3 12
2 7
输出:
23
1、二维数组动态规划
1.1、Runtime error的版本
状态转移方程:f(i,j) = max(f(i,j),f(i-1,j-ui)+wi)
#include<iostream>
#include<math.h>
#define MAX_N 3402 // 最大种类数
#define MAX_W 400 //最大单一种类重量
#define MAX_D 100 // 最大价值
#define MAX_M 12880 //最大限重
using namespace std;
//dp[k][y] 表示在前k中物品中选择,背包重量不超过y时背包最大价值
int dp[MAX_N+1][MAX_M+1];
int weights[MAX_N+1] ;
int values[MAX_N+1] ;
int main() {
int n,m,i,j;
cin >> n >> m;
for (i = 1; i <= n; i++) {
cin >> weights[i] >> values[i];
}
// (1)初始化
for (i = 0; i <= m; i++) {
dp[0][i] = 0; /*不买东西,装入重量再多的非有价值的情况*/
}
for (i = 0; i <= n; i++) {
dp[i][0] = 0; /*多种东西无价值*/
}
// (2)动态规划
for (i = 1; i <= n; i++) { //对前i个物品
for (j = 1; j <= m; j++) {//对前j个重量
// 在 同个重量级的前i-1个种类组合 和 在(j-weights[i])的情况下利用前k种物品转入获取最大值再加当前物品价值 中选择
if (j < weights[i]) // 这时候背包容量不足以放下第 i 件物品,只能选择不拿,继续考虑前i-1件物品
dp[i][j] = dp[i - 1][j];
else //这时背包容量可以放下第 i 件物品,我们可以考虑拿或者不拿,分析怎样价值更大:
dp[i][j] = max(dp[i - 1][j], dp[i-1][j- weights[i]] + values[i]);
}
}
cout << dp[n][m] << endl;
}
虽然这个算法在公式上没有错误,但是使用的多行是多余的,只需要一行保存就可以,不过,这个版本是《算法设计与分析》课本教的,可能是为了好理解吧
1.2 展示路径
#include<iostream>
#include<math.h>
#define MAX_N 3402 // 最大种类数
#define MAX_W 400 //最大单一种类重量
#define MAX_D 100 // 最大价值
#define MAX_M 12880 //最大限重
using namespace std;
//dp[k][y] 表示在前k中物品中选择,背包重量不超过y时背包最大价值
// 不装第k个物品或者至少装一个第k种物品
int dp[MAX_N+1][MAX_M+1];
int I[MAX_N + 1][MAX_M + 1]; //标志数组,方便回溯,计算优化函数值dp[k][y]时用到物品的最大编号
int weights[MAX_N+1] ;
int values[MAX_N+1] ;
void TrackSolution(int n,int m) {
int x[MAX_N+1] = { 0 }; //n中商品装入量
int y = m, j = n; // I[J][y]
while (I[j][y] != 0) {
j = I[j][y];
while (I[j][y] == j) {
y = y - weights[j];
x[j]++;
}
}
for (int i = 1; i <= n; i++) {
cout << x[i] << " ";
}
cout << endl;
}
int main() {
int n,m,i,j;
cin >> n >> m;
for (i = 1; i <= n; i++) {
cin >> weights[i] >> values[i];
}
// (1)初始化
for (i = 0; i <= m; i++) {
dp[0][i] = 0; /*不买东西,装入重量再多的非有价值的情况*/
}
for (i = 0; i <= n; i++) {
dp[i][0] = 0; /*多种东西无价值*/
}
// (2)动态规划
for (i = 1; i <= n; i++) { //对前i个物品
for (j = 1; j <= m; j++) {//对前j个重量
// 在 同个重量级的前i-1个种类组合 和 在(j-weights[i])的情况下利用前k种物品转入获取最大值再加当前物品价值 中选择
if (j < weights[i])
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i-1][j- weights[i]] + values[i]);
if (j < weights[i] || (dp[i - 1][j] > dp[i][j - weights[i]] + values[i]))
I[i][j] = I[i - 1][j];
else
I[i][j] = i;
}
}
cout << dp[n][m] << endl;
TrackSolution(n, m);
}
2、回溯法
2.1 、Time Limit Exceeded
#include<stdio.h>
#define MAX_M 12880 //最大限重
#define MAX_N 3402 // 最大种类数
int c;//背包容量
int n;//物品种类数量
int weight[MAX_N+1]; //存放n个物品种类的数组
int price[MAX_N+1]; //存放价值的数组
int currentWeight = 0; //当前重量
int currentPrice = 0; //当前价值
int bestPrice = 0; //当前最优值
int bp = 0; //全局最优价值
void Backtracking(int i) {
if (i > n) {
if (bestPrice > bp) {
bp = bestPrice;
}
return;
}
if (currentWeight + weight[i] <= c) {
// 把物品i放入背包,搜索左子树
currentWeight += weight[i];
bestPrice += price[i];
Backtracking(i + 1);
currentWeight -= weight[i];
bestPrice -= price[i];
}
Backtracking(i + 1);
}
int main() {
int i;
scanf_s("%d%d", &n,&c);
for (i = 1; i <= n; i++) {
scanf_s("%d%d", &weight[i], &price[i]);
}
Backtracking(1);
printf("%d\n", bp);
}
2.2 展示路径
#include<stdio.h>
#define MAX_M 12880 //最大限重
#define MAX_N 3402 // 最大种类数
int c;//背包容量
int n;//物品种类数量
int weight[MAX_N+1]; //存放n个物品种类的数组
int price[MAX_N+1]; //存放价值的数组
int currentWeight = 0; //当前重量
int currentPrice = 0; //当前价值
int bestPrice = 0; //当前最优值
int bestAnswer[MAX_M]; //当前最优解
int bp = 0; //全局最优价值
int bA[MAX_M+1]; //全局最优路径
void Backtracking(int i) {
if (i > n) {
if (bestPrice > bp) {
bp = bestPrice;
for (int j = 1; j <= n; j++) {
bA[j] = bestAnswer[j];
}
}
return;
}
if (currentWeight + weight[i] <= c) {
// 把物品i放入背包,搜索左子树
bestAnswer[i] = 1;
currentWeight += weight[i];
bestPrice += price[i];
Backtracking(i + 1);
currentWeight -= weight[i];
bestPrice -= price[i];
}
bestAnswer[i] = 0;
Backtracking(i + 1);
}
int main() {
int i;
scanf("%d%d", &n,&c);
for (i = 1; i <= n; i++) {
scanf("%d%d", &weight[i], &price[i]);
}
Backtracking(1);
printf("%d\n", bp);
for (i = 1; i <= n; i++)
printf("%d ", bA[i]);
}
3、一维数组动态规划 AC
注意,for (int j = m; j >= weights[i]; j--)
是逆序遍历总重量
// 一维数组解法
#include<iostream>
#define MAX_M 12880 //最大限重
#define MAX_N 3402 // 最大种类数
using namespace std;
int dp[MAX_M + 1];
int weights[MAX_N + 1];
int values[MAX_N + 1];
int main() {
int n, m, i, j;
cin >> n >> m;
for (i = 1; i <= n; i++) {
cin >> weights[i] >> values[i];
}
for (int i = 1; i <= n; i++) { // 种类
for (int j = m; j >= weights[i]; j--) { //容量
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);
}
}
cout << dp[m] << endl;
}