问题描述:
给了n种物品和
一个背包。物品的重量的W[i](weight),价V[i](value),背包容量为c(capacity),问应该如何选择放入背包中的物品,使得装入背包的物品总价值最大。
∑x[i]*w[i]<=c
1→n
且,价值∑x[i]*v[i]最大
设(y1,y2,y3,……Yn)是所给的0-1背包问题的一个最优解。
那么(
y2,y3,……Yn)必须是
∑y[i]*w[i]<=c - w[1]y1
2→n
且,价值∑y[i]*v[i]最大
如果w[1]=0相当于没装,就是转换成2→n的物品放到容量c背包中,如果等于1,则该子问题就变成了去掉第 一个物品重量的背包问题
如果(
y2,y3,……Yn)不是上述子问题的一个最优解,那么假设最优解为
(z2,z3,……Zn)
那么
∑z[i]*v[i]才是该子问题的求出来的最大价值,
那么原问题,最优解,就变成了:
x[1]*v[1] + ∑z[i]*v[i] 2→n
那么得到的这个价值肯定比原先假设的最优解的价值还要大,很明显,这是与假设有矛盾的。
so,
(
y2,y3,……Yn)必须是0-1背包子问题的一个最优解!!
m[i][j]
代表 i→n物品,背包容量为j,0-1背包问题的最优解
w[i]重量 v[i]价值 c背包容量
不放第i个物品:转成
,i+1 →物品放入容量为j的背包中的最大价值
放第i个物品:转成,
i+1 →物品放入容量为j-w[i]的背包中的最大价值+v[i]
.
j<w[i]
背包剩余的容量已经放不下了
则:m[i][j] = m[i+1][j]
m[n][j] = v[n]
能放下
j>=w[n]
0
放不下了
0<j<w[n]
然后就是具体的实现过程
最后得到的m[1][c]就是所要求的最优值。
每个物品只有两种可能的选择,放或者不放,所以称为0-1背包问题,
即要找到一个数组(x1,x2,……,Xn),x[i]∈{0,1},不放就是0,放入就是1
要求就是,
先看0-1背包问题是否满足最优子结构性质:
最优子结构的证明基本都是使用反证法来证明。
然后,就是递归关系了
一.j>=w[i]剩下的背包容量可以放下
二.只有第n个物品的时候
举个具体例子:
n=5,c=10,w={2,2,6,5,4},v={6,3,5,4,6}。
代码:
C++语言:
#include<iostream>
using namespace std;
int MAX( int a , int b)
{
return
a
>b
?
a:b;
}
template < class Type >
void Knapsack( Type * v , int * w , int c , int n , int ** m);
template < class Type >
void Traceback( Type ** m , int * w , int * x , int n , int c);
int main()
{
int n;
//物品个数
int
c;
//背包容量
int
i
,
j;
cout
<<
"请输入物品个数:";
cin
>>n;
cout
<<
"请输入背包容量:";
cin
>>
c;
int
*
w
=
new
int
[n
+
1
];
//每个物品的重量。w[0]什么都不存,从1到n计算
int
*
v
=
new
int
[n
+
1
];
//价值
int
*
x
=
new
int
[n
+
1
];
//用于存放入或者不放的数组
cout
<<
"请输入每个物品的重量和价值:(中间用空格隔开)"
<<
endl;
for(
i
=
1;
i
<n
+
1;
i
++)
cin
>>
w
[
i
]
>>
v
[
i
];
int
**
m
=
new
int
*
[n
+
1
];
//动态建立二维数组分配空间
for(
i
=
0;
i
<n
+
1;
i
++)
m
[
i
]
=
new
int
[
c
+
1
];
Knapsack(
v
,
w
,
c
,n
,
m);
Traceback(
m
,
w
,
x
,n
,
c);
cout
<<
"能装的最大价值是:"
<<
m
[
1
][
c
]
<<
endl;
cout
<<
"可以装入的物品编号,物品重量和对应的价值是:"
<<
endl;
for(
i
=
1;
i
<n
+
1;
i
++)
if(
x
[
i
]
==
1)
cout
<<
"第"
<<
i
<<
"个物品:"
<<
w
[
i
]
<<
" "
<<
v
[
i
]
<<
endl;
return
0;
}
//cout<<"hello,bug!"<<endl;
template < class Type >
void Knapsack( Type * v , int * w , int c , int n , int ** m)
{
int
i
,
j;
int
jMax
=
MAX(
w
[n
]
-
1
,
c);
if(
jMax
>
c)
jMax
=
c;
for(
j
=
0;
j
<=
jMax;
j
++)
m
[n
][
j
]
=
0;
for(
j
=
w
[n
];
j
<=
c;
j
++)
m
[n
][
j
]
=
v
[n
];
//上面是对于m[n][j]的处理
for(
i
=n
-
1;
i
>
1;
i
--)
{
jMax
=
MAX(
w
[
i
]
-
1
,
c);
if(
jMax
>
c)
jMax
=
c;
for(
j
=
0;
j
<=
jMax;
j
++)
m
[
i
][
j
]
=
m
[
i
+
1
][
j
];
//不能选择i的情况
for(
j
=
w
[
i
];
j
<=
c;
j
++)
m
[
i
][
j
]
=
MAX(
m
[
i
+
1
][
j
],
m
[
i
+
1
][
j
-
w
[
i
]]
+
v
[
i
]);
//m[i+1][j]代表能放入第i个数,但是不放入。
//m[i+1][j-w[i]]+v[i]代表放入了第i个数,至于前面能放入的数就看m[i+1][j-w[i]]的值了
m
[
1
][
c
]
=
m
[
2
][
c
];
if(
c
>=
w
[
1
])
m
[
1
][
c
]
=
MAX(
m
[
2
][
c
],
m
[
2
][
c
-
w
[
1
]]
+
v
[
1
]);
//因为第1这一行是属于没用到的子问题,所以就分开处理
}
}
template < class Type >
void Traceback( Type ** m , int * w , int * x , int n , int c)
{
int
i;
for(
i
=
1;
i
<n;
i
++)
if(
m
[
i
][
c
]
==
m
[
i
+
1
][
c
])
x
[
i
]
=
0;
//物品没装
else
{
x
[
i
]
=
1;
c
-=
w
[
i
];
//物品装了,减去这一段的重量
}
x
[n
]
= (
m
[n
][
c
])
?
1
:
0;
//如果m[n][c]为0的话,显然没放,不然就放了
}
using namespace std;
int MAX( int a , int b)
{
}
template < class Type >
void Knapsack( Type * v , int * w , int c , int n , int ** m);
template < class Type >
void Traceback( Type ** m , int * w , int * x , int n , int c);
int main()
{
}
//cout<<"hello,bug!"<<endl;
template < class Type >
void Knapsack( Type * v , int * w , int c , int n , int ** m)
{
}
template < class Type >
void Traceback( Type ** m , int * w , int * x , int n , int c)
{
}