哎,以前算法没有学好,现在找工作笔试动态规划又是必考,其中背包问题又经常遇见,今天给大家介绍几种常见的背包问题。如有误解请大家指导,谢谢。
先说一下算法的主要思想,利用动态规划来解决。每次遍历到的第i个物品,根据w[i]和v[i]来确定是否需要将该物品放入背包中。即对于给定的n个物品,设v[i]、w[i]分别为第i个物品的价值和重量,maxw为背包的容量。再令v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值。则我们有下面的结果:
(1)v[i][0]=v[0][j]=0;
(2)v[i][j]=v[i-1][j] 当w[i]>j
(3)v[i][j]=max{v[i-1][j],v[i-1][j-w[i]]+v[i]} 当j>=w[i]
好的,我们的算法就是基于此三个结论式。
01背包(二维数组求解):
public class _01背包 {
public static void main(String[] args) {
int[] w = {2,1,6,4,3};//物品重量
int[] v = {4,3,5,1,2};//物品价值
int maxw = 12;//背包容量
int len = v.length;//物品个数
int[][] f = new int[len+1][maxw+1];
int i,j;
for(i = 1; i < f.length; i++)
{
for(j = 1; j < f[0].length; j++)
{
if(w[i-1] > j)
f[i][j] = f[i-1][j];
else {
if(f[i-1][j] < f[i-1][j-w[i-1]] + v[i-1])
{
f[i][j] = f[i-1][j-w[i-1]] + v[i-1];
}
else {
f[i][j] = f[i-1][j];
}
}
}
}
for(i = 0; i < f.length; i++)
{
for(j = 0; j < f[0].length; j++)
System.out.print(f[i][j] + " ");
System.out.println();
}
}
}
如果题目要求给出是哪几个物品放入其中,则我们在代码里面加入一个标记数组:
public class sf {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] weight = {3,5,2,6,4}; //物品重量
int[] val = {4,4,3,5,3}; //物品价值
int m = 12; //背包容量
int n = val.length; //物品个数
int[][] f = new int[n+1][m+1]; //f[i][j]表示前i个物品能装入容量为j的背包中的最大价值
int[][] path = new int[n+1][m+1];
//初始化第一列和第一行
for(int i=0;i<f.length;i++){
f[i][0] = 0;
}
for(int i=0;i<f[0].length;i++){
f[0][i] = 0;
}
//通过公式迭代计算
for(int i=1;i<f.length;i++){
for(int j=1;j<f[0].length;j++){
if(weight[i-1]>j)
f[i][j] = f[i-1][j];
else{
if(f[i-1][j]<f[i-1][j-weight[i-1]]+val[i-1]){
f[i][j] = f[i-1][j-weight[i-1]]+val[i-1];
path[i][j] = 1;
}else{
f[i][j] = f[i-1][j];
}
//f[i][j] = Math.max(f[i-1][j], f[i-1][j-weight[i-1]]+val[i-1]);
}
}
}
for(int i=0;i<f.length;i++){
for(int j=0;j<f[0].length;j++){
System.out.print(f[i][j]+" ");
}
System.out.println();
}
int i=f.length-1;
int j=f[0].length-1;
while(i>0&&j>0){
if(path[i][j] == 1){
System.out.print("第"+i+"个物品装入 ");
j -= weight[i-1];
}
i--;
}
}
}
一维数组(无需装满):
public class _01背包一维数组无需装满 {
public static void main(String[] args) {
//int[] weight = {3,5,2,6,4}; //物品重量
//int[] val = {4,4,3,5,3}; //物品价值
int[] w = {3,5,2,6,4};
int[] v = {4,4,3,5,3};
int maxw = 12;
int[] f = new int[maxw+1];
int i,j;
for(i = 0; i < f.length; i++)
f[i] = 0;
for(i = 0; i < v.length; i++)
{
for(j = f.length - 1; j >= w[i]; j--)
{
//f[j] = f[j] > (f[j-w[i]] + v[i]) ? f[j] : (f[j-w[i]] + v[i]);
f[j] = Math.max(f[j], f[j-w[i]] + v[i]);
}
}
for(i = 0; i < f.length; i++)
System.out.print(f[i] + " ");
System.out.println();
}
}
一维数组(装满):
public class _01背包需装满 {
public static void main(String[] args) {
int[] w = {3,5,2,6,4};
int[] v = {4,4,3,5,3};
int maxw = 3;
int[] f = new int[maxw+1];
int i,j;
for(i = 1; i < f.length; i++)
f[i] = Integer.MIN_VALUE;
for(i = 0; i < v.length; i++)
for(j = f.length - 1; j >= w[i]; j--)
f[j] = f[j] > (f[j-w[i]] + v[i]) ? f[j] : (f[j-w[i]] + v[i]);
//f[j] = Math.max(f[j], f[j-w[i]] + v[i]);
for(i = 0; i < f.length; i++)
System.out.print(f[i] + " ");
}
}
注:装满和不装满主要区别是在与它们起始数组的初始化。无需装满初始化全为0,而需要装满的初始化f[0] = 0;其余各项1,2…n则置为Integer.MIN_VALUE,你可以这样理解,全部装满只有在一个物品重量为0时才能装入其它的则无需定义。
完全背包:
完全背包和01背包思想一样,只是先从物品重量开始计数,01背包是以从0开始计数,重量作为判断。
public class _完全背包 {
public static void main(String[] args) {
int[] w = {3,4,6,2,5};
int[] v = {6,8,7,5,9};
int maxw = 10;
int[] f = new int[maxw+1];
int i,j;
for(i = 0; i < v.length; i++)
for(j = w[i]; j <= f.length-1; j++)
f[j] = f[j] > f[j-w[i]] + v[i] ? f[j] : f[j-w[i]] + v[i];
for(i = 0; i < f.length; i++)
System.out.print(f[i] + " ");
}
}
以上纯属个人见解,如有错误求各位大佬不吝指教。