题目来源
作者: 陈晓梅
单位: 广东外语外贸大学
时间限制: 400 ms
内存限制: 64 MB
代码长度限制: 16 KB
问题描述
给定 n ( n < = 100 ) n(n<=100) n(n<=100)种物品和一个背包。物品 i i i的重量是 w i w_i wi,价值为 v i v_i vi,背包的容量为 C ( C < = 1000 ) C(C<=1000) C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品 i i i装入多次,也不能只装入部分物品 i i i。
输入格式:
共有
n
+
1
n+1
n+1行输入: 第一行为
n
n
n值和
c
c
c值,表示
n
n
n件物品和背包容量
c
c
c; 接下来的
n
n
n行,每行有两个数据,分别表示第
i
(
1
≤
i
≤
n
)
i(1≤i≤n)
i(1≤i≤n)件物品的重量和价值。
输出格式:
输出装入背包中物品的最大总价值。
输入样例:
在这里给出一组输入。例如:
5 10
2 6
2 3
6 5
5 4
4 6
输出样例:
在这里给出相应的输出。例如:
15
思路分析
这是一个K-背包问题,这里先列举一个例子解释下什么是K-背包问题。假设有一个小偷在商店里偷东西,但他的背包大小有限,只能一部分商品带走。每个商品不一样贵,问小偷如何赚的最多。如图所示,假设商店里有4件商品,每件的重量分别是3,5,4,9,价格分别是4,8,5,10。小偷的背包容纳的重量不超过20,所以不能把所有商品拿走,必须选择拿或不拿。
我们用如下的关系来表示:
从关系式中可以看出,从任意一个状态
B
(
k
,
w
)
B(k,w)
B(k,w)开始,依次选择是否偷每个商品,如果不偷,则丢弃这个商品,对下个商品进行选择,如果偷,则比较偷和不偷的收益,其中最大的收益就是
B
(
k
,
w
)
B(k,w)
B(k,w)的收益。
由于选择第
k
k
k个商品的收益时需要考虑第
k
−
1
k-1
k−1个商品,因此要计算收益
B
B
B在
(
k
,
w
)
(k,w)
(k,w)所有情况下的取值。因此可以建立二重循环,从
k
=
1
k=1
k=1,
w
=
1
w=1
w=1开始,分别计算
B
(
k
i
,
w
j
)
B(k_i,w_j)
B(ki,wj)。最终要求的结果就包含在这些
B
(
k
i
,
w
j
)
B(k_i,w_j)
B(ki,wj)中。
算法实现
java
import java.io.*;
import java.util.Scanner;
public class Main {
public static void main(String[] argc) throws IOException
{
Scanner sc = new Scanner(System.in);
int N = sc.nextInt()+1;
int W = sc.nextInt()+1;
//要预留1位0
int[] w = new int[N];
int[] v = new int[N];
for (int i=1;i<N;i++) {
w[i]=sc.nextInt();
v[i]=sc.nextInt();
}
System.out.println(knapsack(N,W,w,v));
sc.close();
}
public static int knapsack(int N, int W, int[] w, int[] v) {
int k, C;
int[][] B = new int[N][W];// B自动初始化为0
// k表示商品总数,在这里用于模拟从0到N的情况
// C表示当背包容纳的重量,在这里C从1累加到W,用于模拟不同的背包大小
for (k = 1; k < N; k++) {
for (C = 1; C < W; C++) {
if (w[k] > C) {// 如果第k件太重,则不偷,B不变
B[k][C] = B[k - 1][C];
} else {
int value1 = B[k - 1][C - w[k]] + v[k]; // 偷第k件的总收益
int value2 = B[k - 1][C]; // 不偷第k件的总收益
if (value1 > value2) {
// 选择偷和不偷的最大值
B[k][C] = value1;
} else {
B[k][C] = value2;
}
}
}
}
return B[N-1][W-1];
}
}