首先背包问题,属于动态规划问题,动态规划问题我们主要是找出动态转移方程!
我们用一道例题讲解:
让我假设现在的背包的容量是 C=10
物品编号:1 2 3
物品重量:5 6 4
物品价值:20 10 12
用v[i]表示物品价值,w[i]表示物品重量,要使得放入背包的物品价值最大化,我们知道用贪心是不行的!
动态转移方程:dp[i][j]=max( (dp[i-1][ j−w[i] ] ) + v[i] , dp[i-1][j])
定义状态dp[i][j],我解释一下这个数组
我们暂时可以理解为,我可以放入前i个物品,我背包可承受总重量为j。
(注意可以放入前i个物品,不代表前i个物品都放入了!)
那么dp[i][j]表示当前这个包(解锁了第i件物品,重量承载量为j)的总价值!
我们模拟一下:
当i=1,j<5,证明我可以放入第1件物品,代表第一件物品,我解锁了,可以拿走,只要我当前可承载重量j>w[1]即可装入包中!那么我们发现w[1]=5,当j<5时,我背包承受不住,所以放不下第一件物品,所以j<5时 da[i][j]=0;
当j>5时,我们可以装下第一件物品,要注意我只解锁了i=1第一件,没解锁其他件物品,所以当j>5时,dp[1][j]=20;
我们再接再厉,模拟i=2的情况!
当i=2时,我们的背包解锁了第二件物品,证明我的包既可以装下第一件物品,也可以装下第二件物品,只要我的承载量j够,就可以都带走,不然我们要选择价值最大的装进去!
我们可以看到第二件物品的重量w[2]=6,所以当j<6时,我们的包无法承载第二件,所以j<6,我们只能拿第一件物品,所以j<6时,dp[2][j]=20;
当j>6时,我们回顾一下动态转移方程:dp[i][j]=max( (dp[i-1][ j−w[i] ] ) + v[i] , dp[i-1][j]),j>6我们可以从第一件和第二件选一件拿,但是无法都带走,我们怎么从代码实现呢,就是动态转移方程的逻辑,我们可以从现在时,和过去时来理解这个方程
左边的dp[i][j],代表我目前的背包,已经解锁了i件物品,可承载的重量为j,max函数中第一个dp[i-1][j-w[i]],代表过去的背包,只解锁了第i-1件物品但是只装了j-w[i]的重量的物品,
我在这里多说一句,无论实际有没有物品装在背包里,我们都当已经装了重量为j的物品对待,承重量为 j-w[i]的包里可能一件物品也没有装,但是我们默认装了j-w[i],这样方便理解!
然后我在加上当前第i件物品的v[i]即dp[i-1][j-w[i]]+v[i],就是目前我装下了第i件物品的总价值,max函数第二个dp[i-1][j],代表过去同样装了重量为j的物品,但是只解锁了i-1件物品产生的总价值,我们进行比较选出最大价值的!
举个例子,dp[2][9]=max(dp[1][9-6]+v[2],dp[1][9])我们可以算出
(dp[1][3]+10=0+10=10)<dp[1][9]=20所以我们dp[2][9]=dp[1][9]=20;
所以我们很容易得出当i=2时,只要j>5dp[2][j]=20,j<5dp[2][j]=0
最终的背包一定是最优解答即d[m][v]
我给大家拉一个表格,把每一种情况都写下来给大家!
每一个窗格,代表dp[i][j],所以的dp[i][j]都依赖于它的上一行!
代码:
#include <iostream>
using namespace std;
int w[105],v[105];
int dp[105][1005];
int main()
{
int t,m,res;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&w[i],&v[i]);
}
for(int i=1;i<=m;i++)
{
for(int j=t;j>=0;j--)
{
if(j>=w[i])//只有当j>当前w[i]它才有选择的权力
{
dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
printf("%d",dp[m][t]);
return 0;
}
下面是01背包的优化以及,完全背包大家可以学习一下!
01背包二维到一维优化
完全背包
谢谢观看,多多点赞支持!