0-1背包问题通常情况下物品的分量是整数的,选用动态规划能够处理,在处理物品分量非整数情况下的背包问题之前,咱们先来回忆整数背包问题,并从中寻觅处理非整数背包问题的方法。

算法工程师学什么专业题界说:有n种物品和一个容量为cc的背包,第ii件物品的分量为wiwi,价格为vivi,求出哪种物品数组词组合放入背包使物品价值总和最大。element是什么牌子

整数0-1背包问题

p(i,j)p(i,j)标明在容量为j情况下,将物品i,i+1…ni,i+1…n组合的放入背包的最算法开始优解的值

算法与数据结构其转移方程为

假定 j>=wij>=w_ip(i,j)=max(数组词p(i+1,j),p(i+1算法与数据结构,j−wi)+vi)p算法是什么(i,j) = max(p(i+1,j), p(i+1,j-w_i )+v_i )

假定 j<wij<w_i ,数组函数的使用方法 p数组去重(i,j)=p(i+1,j)p(i,j) = p(i+1,j)

能够理解为当j<wElementij<w_i ,背包无法放下第ii件物数组品,所以其最优解和考虑放i+1i+1nn的物品到容量为j的背包的最优解相同。当j>=wij>=w_i时,能够选择放和不放第ii件物品,当不放时背包价值和p(i+1,j)p(i+1,j)相同,当放时背包价值为p(i+1,j−wi)p(i+1,j-w_i )再加上第ii件物品的价值。

所以整数背包问题能够选用动态规划处理,开荒n∗cn*c数组,从下数组的界说往上不断更新数组的值,得到p(i,j)p(i,j)的值,最数组词三声优解便是p(0,c)p(0,c)的值

pelementary是什么意思ublic static int solution(int c, int[] v, int[] w){
if(v.length != w.length){
throw new IllegalArgumen数组初始化tException();
}
int n = v.length;int m = c+1;
int[][] result = new int[n][m];
for(int i = 0;i < m;i++){
result[n-1][i] = i>=w[n-1]? v[n-1Element]:0;
}算法的概念
for(int i = n-2;i&gelementaryt;=0;i--){
for(int j = 0;j < m;j++){
if(j < w[i]){
result[i][j] = resul数组t[i+1][j];
}else {
result[i][j] = Math.max(reelement滑板sult[i+1][j], result[i+1][j-w[i]算法工程师]+v[i]);
}
}
}
traceBack(c, v, w, result);
return result[0][c];
}

途径回溯,找到最优组合

prelementanimationivate static void traceBack(int c, int[]v, int[] w, int[][] p){
int k = c算法;
List<Integer> trace = new ArrayList<>();
int i;
for(i = 0;i < p.length-1;i++){
if(p[i][k] == p[i+1][k-w[i]]+v[i数组函数的使用方法]){
k = k - w[i];算法导论
trace.add(i+1);
}
}
if(p[i][k] == v[i]){
trace.add(i+算法的概念1);
}
System.out.println(trace);
}

整数0-1背包问题的改善

例如背包的容量为elements10,5算法是什么件物品的分量分别为2,2,6,5,4 ,对应算法工程师数组初始化值分别是6,3,5,4,6,则p(i,j)​p(i,j)​数组的更新算法的概念情况如下

weight value 1 2 3 4 5 6 7 8 9 10
2 6 0 6 6 9 9 12 12 15 15 15
2 3 0 3 3 6 6 9 9 9 10 11
6 5 0 0 0 6 6 6 6 6 10 11
5 4 0 0 0 6 6 6 6 6 10 10
4 6 0 0 0 6 6 6 6 6 6 6

当背包的容量很大(即c的值特别大),则这个数组将会失常element是什么牌子的巨大,并且数组的每一个数都需求更新,算法需求的核算时刻较多。

所以能够进行恰当的改善,从上面的表数组指针格来看,咱们无需记载数组种的每一elementary怎样读音个数,只需求记载数组指针下每行的跳动点即可,比方终数组词究一行从第4列初步跳动,咱们只element滑板需记载{(0,0),(4,6)}即可,数组的界说倒数第二行只需记载{(0,0),(4,6),(9,10)}即可。接下来的问题是怎样算法工程师需求把握什么更新每行的跳动点。

p[5] = {(0,0),(4,6)}

将p[5]的跳动点加上下一个需求放入的物品的分量和价值,则能够得到q[5] = p[5]+(5,4) = {(5,4),(9,算法导论10)},

将p[5]和q[5]兼并得到{数组指针(0,0),(4,6),(5,4),(9,10)},之后去除分量大却价值小的点,如(5,4)点,elementary怎样读音放入背包的物品总分量大于4,价值却只有4小于6,所以通过比较(5,4)和(4,6)需求去掉(5,4)得到p[4] = {(0,0),(4,6),(9,10)}

非整数0-1背包问题

非整数0-算法的概念1背包问题能够转化为整数0-1背包问题,假定非整数能够用保存三位小数来标明的话,那么能够将非整数背包问题的一切值乘上1000,悉数转为整数,选用整数背包问题处理,可是这样有必要牺算法与数据结构牲必定的精度,并且会增加开荒数组的巨细(乘上1000,数组的列必定跨越1000以上),这对核算时刻也有很大影响,因为需求更新每一个数组中的元素。

有用的处理方法和上述关于整数0-1背包问题的改善elementary翻译是相同的,并且发现上述算法开始知识点的跳动点并不要求elementary翻译是整数,关于实数相同适用,因此能够选用更新跳动点的动态规划的方法处理非整数0-1背包问题。

public static double solution(double c, double[] v, doub数组去重le[] w){
if(v.length != w.length){
throw new IllegalArgume数组词三声ntException();
}
double[][] p = new do数组初始化uble[10000][2];
int n = v.length;
p[0][0] = 0;p[0][1] = 0;
int数组指针 left = 0, right = 0,next = 1;
int[]数组词 head = new int[n+2];
head[n+1] = 0;
head[n]数组初始化 = 1;
for(element是什么牌子int i = n-1; i >= 0;i--){算法统宗
int k = left;
fo数组的界说r(int j = left; j <= right;j++){
if(p[j][0] + w[i] > c){
break;
}
double nw = p[j][0] + w[i];
d数组词多音字组词语ouble nv = p[j][1] + v[i];
//放入比nw小的跳动elementary点,因为分量小的价值不管巨细
for(;k<=right && p[k][0] < nw;k++,nex数组t++){
p[数组词三声next][0] = p[k][0];
p[next][1] = p[k][1];
}
//假定分量相等,数组的界说取价值大的跳动点
if(k <= right && p[k][0] == nw){elements
if(p[k][1] > nv){
nv = p[k][1];
}
k++;
}
//放入更新的跳动点
if(nv >数组的界说 p[next-数组c言语1][1]){
p[next][0] = nw;
p[next][1] = nv;
next++;
}
/*去除比更新的跳动点分量大却价值小的点,
由所以每数组词三声一次更新完之后成果都是分量和价值都是递增的跳动点排elementanimation列
一旦出现价值跨越当时的点,那后续数组词的点必定都是跨越的*/
for(;k <= righ数组去重t &&elements p[k][1] <= nv;k++);
}
//将后续的点放入
f算法的概念or数组去重 (;k <= right; k++,next++){
p[next][0] = p[k][0];
p[next]Element[1算法统宗] = p[k][1];
}
left = right+1;right = next - 1;head[i] = next;
}
traceBack(v, w, p, head);
return p[next-1][1];
}

途径回溯,找到最优组合

private static void traceBack(double[] v, double[] w, double[][] p, int[] head){
List<Integer> trace = new ArrayList&lelementst;>();
int k = head[0]-1elementary怎样读音;
int n = w.length;
for(int i = 1;i <= n;i+数组指针+){
int left = head[i+1];
int right = head[i]-1;
for(int j = lef数组指针t;j<=right;j++){
if(p[j][0] + w[i-1] ==  p[k][0]算法工程师需求把握什么 && p[j][1] + v[i-1] ==elementui p[k][1数组公式]){
k = j;
trace.add(i);
break;
}
}
}
System.out.prelementaryintln(trace);
}

假定不考虑放入哪些物品(即不考虑途径回溯),只关怀最优解elementary怎样读音的值,能够只存放当时的跳动点集和下一步的跳动点集,无需记载每一步的跳动点集

public static算法与数据结构 double solut算法的概念ion2(double c, doubl算法工程师e[] v, do算法开始知识点uble[] w){
i算法工程师f(v.length != w.length){
throw new Illegalelementary翻译Arguelement什么意思中文mentException();
}
int n = v.length;
List<double[]> p = new ArrayList<>();
p.add(new d算法工程师需求把握什么ouble[]{0, 0});
for(int i = n - 1; i >= 0;i--){
int k = 0;
Lis算法的概念t<double[]> q = new ArrayList<>();
for算法统宗(double[] element: p){
if(w[i] +数组指针 element[0] > c){
break;
}
double nw = w[i] + eelementslement[0];
double nv = v[i] + element[1];
for(;k < p.size() && p.get(k)[0]数组的界说 < nw;k++){
q.add(p.get(k));
}
if(k < p.size() && p.get(k)[0] == nw){
if(p.get(k)[1] &gt数组词多音字组词语; nv){
nv = p.get(k)[1];
}
k++;
}
if(nv &gt数组公式; q.g数组的界说et(q.size()-1)[1]){
q.add(new double[]{nw, nv});
}
for(;kelementary翻译 < p.size() && p.get(k)[1] < nv;k++);
}
foelementary是什么意思r(;k < p.size();k++){
q.add(p.get(k));
}
p = q;
}
return p.get(p.size()-1)[1];
}