算法复习——01背包

01背包

算法复习——01背包_第1张图片
DP分析法要素有:集合,属性,状态计算
(集合是指只考虑前i个,总体积小于等于j的所有选法,存取的属性是所有选法的最大值)
算法复习——01背包_第2张图片
状态方程计算(所有选法可以分为2种不同的子集)
算法复习——01背包_第3张图片
左边子集的属性:不含有第i个物品,所以表示为 f ( i − 1 , j ) f(i-1,j) fi1j
右边子集的属性:含有第i个物品(间接计算一下),
表示为 f ( i − 1 , j − v [ i ] ) + w j f(i-1,j-v[i])+w_j fi1jv[i]+wj
f ( i , j )的属性是上述二者的最大值 f(i,j)的属性是上述二者的最大值 fij)的属性是上述二者的最大值
代码如下:

#include 
#include 
using namespace std;
int v[1010];
int w[1010];
int dp[1010][1010];
int main ()
{
    int i,j,n,m;
    cin>>n>>m;
    for(i=1;i<=n;i++)  cin>>v[i]>>w[i];//输入容积和价值
    for(i=1;i<=n;i++)//此时dp[0][j]一定是0
    {
        for(j=0;j<=m;j++)
        {
            if(j>=v[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);//特判j大于v[i]的情况
            else dp[i][j]=dp[i-1][j];
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}

ps:真正的初始化代码(只考虑前1个)应该是

for(j=1;j<=m;j++)
{
     if(j>=v[1]) dp[1][j]=w[1];
     else dp[1][j]=0;
}

而后i从2再开始进行状态计算,上述代码可以等价这个初始化代码,其他DP的题目的时候要注意DP的初始化。

01背包一维优化

优化的思路(滚动数组的优化方法):
①dp数组优化成一维的,dp[i]代表体积不超过i的最大的价值
②一开始初始化的时候代表考虑前0个物品也就是全为0,第i轮代表考虑前i个物品
③第二轮扫描的时候从后面开始(为了不发生错误),并且只用扫描j>=v[i]的情况,因为不是这种情况的话根本没有更新的必要

#include 
#include 
using namespace std;
int v[1010];
int w[1010];
int dp[1010];
int main ()
{
    int i,j,n,m;
    cin>>n>>m;
    for(i=1;i<=n;i++)  cin>>v[i]>>w[i];
    for(i=1;i<=n;i++)
      for(j=m;j>=v[i];j--)
         dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    cout<<dp[m]<<endl;
    return 0;
}

一些背包问题的拓展

(1)完全背包问题:物品的个数变成了无限个,那么加入背包容量是m且最多可以装下k个大小是v[i]的物品,那么按照DP分析法可以分为k+1个集合(如下所示):
算法复习——01背包_第4张图片
则状态转移方程可以表示为
代码如下

#include 
#include 
using namespace std;
int v[1010];
int w[1010];
int dp[1010];
int main (void)
{
    int i,j,n,V;
    cin>>n>>V;
    for(i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(i=1;i<=n;i++)
      for(j=v[i];j<=V;j++)
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
     cout<<dp[V]<<endl;
     return 0;
}

你可能感兴趣的:(算法准备,算法,动态规划)