01背包 : 在 N 件物品,每件物品的重量为data[1][0],data[2][0]……data[n][0],与之相对应的价值为data[1][1],data[2][1]……data[n][1], 取出若干件放在 能装重量为 M 的背包里,求能装的最大价值为?
C 版:
#include <stdio.h>
#include <stdlib.h>
int max(int a,int b)
{
if (a>b)
return a;
else return b;
}
int main()
{
int n,m,i,j;
int data[1001][2];
int f[1001][1001]; //最优值矩阵,f[n][m]为最后所装最大价值 即 最优值
scanf("%d%d",&n,&m); // n 表示n件物品,m 表示背包能背的最大重量 即 总承重
for(i=1;i<=n;i++)
{
scanf("%d%d",&data[i][0],&data[i][1]); //每件物品的重量,价值
}
for(i=0;i<=m;i++)
f[0][i]=0;
for(i=0;i<=n;i++)
f[i][0]=0;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
f[i][j]=0;
if(j>=data[i][0]) //如果第 i 个物品重量 小于等于 总承重
{
f[i][j]=max(f[i-1][j],f[i-1][j-data[i][0]]+data[i][1]);
//按 f[i-1][j] 和 f[i-1][j-data[i][0]]+data[i][1] 哪个价值大,决定这件物品 是否被选择
//f[i-1][j-data[i][0]]+data[i][1] 即 j-data[i][0]的重量能背的最大价值 + 这件物品的价值
//f[i-1][j] 即 不装这件物品时的重量能背的最大价值
}
else
f[i][j]=f[i-1][j]; //如果第 i 个物品重量大于总承重
}
}
printf("%d\n",f[n][m]);
system("pause");
return 0;
}
测试输入:
8 13
3 20
4 8
2 11
5 15
3 6
1 7
1 12
4 18
输出:
68
Java版:
Goods.java
public class Goods {
private int weight;
private int value;
public Goods(int weight,int value){
this.weight = weight;
this.value = value;
}
public int getWeight() {
return weight;
}
public int getValue() {
return value;
}
public String toString(){
return "[weight: " + weight + " " + "value: " + value + "]";
}
}
KnapsackProblem.java
import java.util.ArrayList;
public class KnapsackProblem {
private Goods[] goods; //物品
private int totalWeight; //totalWeight 表示背包能背的最大重量
private int n; // n 表示n件物品
private int[][] bestValues; //最优值矩阵
private int bestValue; //最优值
private ArrayList<Goods> bestSolution;//最优解的物品组成
public KnapsackProblem(Goods[] goods, int totalWeight) {
this.goods = goods;
this.totalWeight = totalWeight;
this.n = goods.length;
if (bestValues == null) {
bestValues = new int[n+1][totalWeight+1];
}
}
public void solve() {
for (int i = 0; i <= n; i++) {
bestValues[i][0] = 0;
}
for (int j = 0; j <= totalWeight; j++) {
bestValues[0][j] = 0;
}
for (int i = 1; i <= n; i++) { //同 C版 描述
for (int j = 1; j <= totalWeight; j++) {
if (j >= goods[i - 1].getWeight()) {
int iweight = goods[i - 1].getWeight();
int ivalue = goods[i - 1].getValue();
bestValues[i][j] = Math.max(bestValues[i - 1][j], bestValues[i - 1][j - iweight] + ivalue);
} else {
bestValues[i][j] = bestValues[i - 1][j];
}
}
}
bestValue = bestValues[n][totalWeight];
if(bestSolution == null){
bestSolution = new ArrayList<Goods>();
}
int tempWeight = totalWeight;
for(int i=n;i >=1;i--){
if(bestValues[i][tempWeight] > bestValues[i-1][tempWeight]){
bestSolution.add(goods[i-1]); // goods[i-1] 表示第 i 个物品
tempWeight -= goods[i-1].getWeight();
}
if(tempWeight == 0){
break;
}
}
}
public int[][] getBestValues() {
return bestValues;
}
public int getBestValue() {
return bestValue;
}
public ArrayList<Goods> getBestSolution() {
return bestSolution;
}
}
测试
KnapsackTest.java
public class KnapsackTest {
public static void main(String[] args) {
Goods[] goods = new Goods[] {
new Goods(3,20), new Goods(4,8),
new Goods(2,11), new Goods(5,15),
new Goods(3,6), new Goods(1,7),
new Goods(1,12), new Goods(4,18)
};
int totalWeight = 12;
KnapsackProblem kp = new KnapsackProblem(goods, totalWeight);
kp.solve();
System.out.println(" -------- 该背包问题实例的解: --------- ");
System.out.println("最优值:" + kp.getBestValue());
System.out.println("最优解【选取的物品】: ");
System.out.println(kp.getBestSolution());
System.out.println("最优值矩阵:");
int[][] bestValues = kp.getBestValues();
for (int i=0; i < bestValues.length; i++) {
for (int j=0; j < bestValues[i].length; j++) {
System.out.printf("%-5d", bestValues[i][j]);
}
System.out.println();
}
}
}
输出:
-------- 该背包问题实例的解: ---------
最优值:68
最优解【选取的物品】:
[[weight: 4 value: 18], [weight: 1 value: 12], [weight: 1 value: 7], [weight: 2 value: 11], [weight: 3 value: 20]]
最优值矩阵:
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 20 20 20 20 20 20 20 20 20 20 20
0 0 0 20 20 20 20 28 28 28 28 28 28 28
0 0 11 20 20 31 31 31 31 39 39 39 39 39
0 0 11 20 20 31 31 31 35 39 46 46 46 46
0 0 11 20 20 31 31 31 37 39 46 46 46 52
0 7 11 20 27 31 38 38 38 44 46 53 53 53
0 12 19 23 32 39 43 50 50 50 56 58 65 65
0 12 19 23 32 39 43 50 50 57 61 68 68 68
时间复杂度为O(n*m),基本已经不能再优化了,但空间复杂度却可以优化到O(v)
改进版(将最优矩阵改为使用一维数组):
C++版:
#include <cstdio>
#include <cstring>
int bestValues[1005];
int main()
{
int n,m;// n 表示n件物品,m 表示背包能背的最大重量 即 总承重
int i,j;
while(scanf("%d %d",&n,&m)&&(n+m))
{
int weight[1001],value[1001];
for (i=1;i<=n;i++)
{
scanf("%d",&weight[i]);
scanf("%d",&value[i]);
}
memset(bestValues,0,sizeof(bestValues));//数组初始化为0
for (i=1;i<=n;i++)//从第一个物品开始一直到第n个物品
for(j=m;j>=weight[i];j--)//从背包装满的状态开始,且第j个背包状态重量应该要大于等于第i个物品的重量
if (bestValues[j-weight[i]]+value[i]>bestValues[j]){//如果第j-1个状态放入第i个的物品,并且放入后总价值大于第j个状态的价值
bestValues[j] = bestValues[j-weight[i]]+value[i]; //则第j-1个状态时选择放入第i个的物品
}
printf("%d\n",bestValues[m]);
}
return 0;
}