一、01背包问题:
问题:01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2……Wn,与之相对应的价值为P1,P2……Pn。在给定容量为C的条件下,求如何选取物体使带来的价值最大化。
在选择物品的时候,对每种物品i只有两种选择,即装入背包或不装入背包。不能讲物品i装入多次,也不能只装入物品的一部分。因此,该问题被称为0-1背包问题。
问题分析:令f(i,j)表示在前i(1<=i<=n)个物品中能够装入容量为j(1<=j<=C)的背包中的物品的最大价值,则可以得到如下的动态规划函数:
(1)V(i,0)=V(0,j)=0
(2)f(i,j)=f(i-1,j) j< wi ;
f(i,j)=max{f(i-1,j) ,f(i-1,j-wi)+vi) } j>wi
(1)式表明:如果第i个物品的重量大于背包的容量,则装人前i个物品得到的最大价值和装入前i-1个物品得到的最大价是相同的,即物品i不能装入背包;第(2)个式子表明:如果第i个物品的重量小于背包的容量,则会有一下两种情况:(a)如果把第i个物品装入背包,则背包物品的价值等于第i-1个物品装入容量位j-wi 的背包中的价值加上第i个物品的价值vi; (b)如果第i个物品没有装入背包,则背包中物品价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值最大的作为把前i个物品装入容量为j的背包中的最优解。
java代码如下:
package dynamic_programming;
import java.util.Scanner;
/** * @author Gavenyeah * * @date Time: 2016年4月22日下午5:41:02 */
//01背包问题,M件物品的重量不同,带来的价值不同,在给定最大重量容量下,找出能带来最大价值的组合
//状态转移方程: f(i,j)=max{f(i-1,j-wi)+vi, f(i-1,j)}
public class Package_0_1 {
int weight[]=null;
int value[] = null;
int C = 0;
int m=0;
public static void main(String[] args) {
Package_0_1 p01 = new Package_0_1();
p01.getInput();
System.out.println(p01.getValue());
}
public int getValue(){
int[][] maxValue=new int [m+1][C+1];//m和C的值加1的目的是避免单独处理i-0和j=0的情况
for(int i = 1; i < m+1; i++){
for(int j = 1; j < C + 1; j++){
if(j>=weight[i])
//根据状态转移方程计算当前最大价值
maxValue[i][j] = Math.max(maxValue[i-1][j-weight[i]]+value[i],maxValue[i-1][j]);
else maxValue[i][j] = maxValue[i-1][j];
}
}
return maxValue[m][C];//返回m个物体,容量为C时的最大价值
}
public void getInput() {//从键盘输入数据
Scanner cin = new Scanner(System.in);
System.out.print("背包最大容量:");
C = cin.nextInt();
System.out.print("请输入物品数:");
m = cin.nextInt();
weight = new int[m+1];
System.out.print("依次输入每件物品的重量:");
for (int i = 1; i < m+1; i++) {
weight[i] = cin.nextInt();
}
System.out.print("依次输入每件物品的价值:");
value = new int[m+1];
for (int i = 1; i < m+1; i++) {
value[i] = cin.nextInt();
}
cin.close();
}
}
二、装箱问题(一维):
问题: 装箱问题:有一个箱子容量为v(正整数,o≤v≤20000),同时有n个物品(o≤n≤30),每个物品有一个体积(正整数)。要求从m个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。
分析:装箱问题与01背包问题差不多,状态转移方程也差不多。
f(i,j)表示前i件物品在给定j体积时所能达到的最大值。
f(i, j)=max(f(i-1,j) , f(i-1, j-vi)+vi)
代码:
package dynamic_programming;
import java.util.Scanner;
/** * @author Gavenyeah * @date Time: 2016年4月23日下午10:40:35 * @des: */
// 装箱问题:有一个箱子容量为v(正整数,o≤v≤20000),同时有n个物品(o≤n≤30),每个物品有一个体积
// (正整数)。要求从m个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。
public class BinPacking {
int volume[] = null;
int max_V = 0;
public static void main(String[] args) {
BinPacking bp = new BinPacking();
bp.getInput();
bp.binPacking();
}
// f(i,j)表示前i件物品在给定j体积时所能达到的最大值
public void binPacking() {
int[][] maxSumVolume = new int[volume.length][max_V + 1];
for (int i = 1; i < volume.length; i++) {
for (int j = 1; j <= max_V; j++) {
if (j >= volume[i]) {
maxSumVolume[i][j] = Math.max(maxSumVolume[i - 1][j],
maxSumVolume[i - 1][j - volume[i]] + volume[i]);
}
}
}
int minSurplus = max_V - maxSumVolume[volume.length - 1][max_V];
System.out.println("最小剩余:" + minSurplus);
}
public void getInput() {// 从键盘输入数据
Scanner cin = new Scanner(System.in);
System.out.print("请输入箱子容量:");
max_V = cin.nextInt();
System.out.print("请输入物品数:");
int m = cin.nextInt();
volume = new int[m + 1];
System.out.print("依次输入每件物品的重量:");
for (int i = 1; i < m + 1; i++) {
volume[i] = cin.nextInt();
}
cin.close();
}
}