#include <iostream>
#include <cstring>
using namespace std;
const int N=15;
int main()
{
int v[N]={0,8,10,6,3,7,2};
int w[N]={0,4,6,2,2,5,1};
int m[N][N];
int n=6,c=12;
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
m[i][j]=m[i-1][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
cout<<m[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
问题描述:0-1背包问题,给定n种物品,和一个容量为C的背包,物品 i 的重量是W[ i ] ,价值为V[ i ] ,问:应该如何选择装入背包的物品,使得装入背包中物品的价值最大?
要求,对于每一个物品,我们只能选择拿与不拿,并不能装入物品的一部分,也不能装入多个同样的物品。
解题思路:在给定空间C的情况下选择 n 个物品中的某些物品,对于每一个物品只有拿与不拿。可以想象在选择第i个物品时可以拿,也可以不拿,如果拿了第i个物品,这时候的价值可以等价于 用C-w[i]的空间选择i-1个物品的价值 + v[ i ] 。 如果不拿,这时候的价值相当于利用C的空间选择i-1个物品的价值(注意,不拿的情况下空间大,最优解不一定小于拿的情况。)。因此,对于第i个物品的选择由前边的两种情况的最大值决定。(这中解题思路是一种逆向思维,是从后边往前走的。而在计算时是由前往后计算的)
建立模型:声明一个大小为m[ n ][ c ]的二维数组,m[i][j]表示,在面对第i件物品的选择时,利用j的空间选择前i件物品能得到的最大值。根据上边的分析很容易得到m[i][j]的计算方法。
(1)如果j < w [ i ] ,这时候背包容量不足以放下第 i 件物品,只能选择不拿,这时候对于m[i][j]的选择只能有 m[ i - 1 ][ j ](在j空间中对前i-1个物品进行最优选择)所决定。
m[ i ][ j ] = m[ i-1 ][ j ]
(2)如果j<w[i],这时候背包容量可以放下第i件物品,我们就要考虑放下这个物品能否创造最大价值,即第i件物品是否在最优解中,因此这时候我们就要比较
m[ i - 1 ][ j ] 和 m[ i-1 ][ c - w[ i ] ] (在c - w[ i ] 的空间中面对i - 1 个物品进行最优选择,这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价 值,也是相当于为第i件物品腾出了w[i]的空间。)大小来判断 i 是否选择,(而在实际计算中我们对于这两项并不了解,因此需要从前往后计算)。
状态转移方程:
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
m[i][j]=m[i-1][j];
例:0-1背包问题:(来源:https://blog.csdn.net/xp731574722/article/details/70766804)
在使用动态规划算法求解0-1背包问题时,使用二维数组m[ i ][ j ]存储 j 空间下面对 i 件物品的最优解 ,而使用动态规划算法求解时其实就是对二维数组m[ i ][ j ]的绘制过程。
价值数组 V[ n ] = {8, 10, 6, 3, 7, 2}
重量数组 M[ n ] = {4, 6, 2, 2, 5, 1} 背包容量为C = 12
(第一行和第一列为序号,其数值为0)
如m[2][6],在面对第二件物品,背包容量为6时我们可以选择不拿,那么获得价值仅为第一件物品的价值8,如果拿,就要把第一件物品拿出来,放第二件物品,价值10,那我们当然是选择拿。m[2][6]=m[1][0]+10=0+10=10;依次类推,得到m[6][12]就是考虑所有物品,背包容量为C时的最大价值。
代码实现:
#include <iostream>
#include <cstring>
using namespace std;
const int N=15;
int main()
{
int v[N]={0,8,10,6,3,7,2};
int w[N]={0,4,6,2,2,5,1};
int m[N][N];
int n=6,c=12;
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
m[i][j]=m[i-1][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
cout<<m[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
到这一步,可以确定的是可能获得的最大价值,但是我们并不清楚具体选择哪几样物品能获得最大价值。
另起一个 x[ ] 数组,x[i]=0表示不拿,x[i]=1表示拿。
m[n][c]为最优值,如果m[n][c]=m[n-1][c] ,说明有没有第n件物品都一样,则x[n]=0 ; 否则 x[n]=1。当x[n]=0时,由x[n-1][c]继续构造最优解;当x[n]=1时,则由x[n-1][c-w[i]]继续构造最优解。以此类推,可构造出所有的最优解。
void traceback()
{
for(int i=n;i>1;i--)
{
if(m[i][c]==m[i-1][c])
x[i]=0;
else
{
x[i]=1;
c-=w[i];
}
}
x[1]=(m[1][c]>0)?1:0;
}
参考(https://blog.csdn.net/xp731574722/article/details/70766804)