34 动态规划DP 背包问题全解

背包问题分析法

   

1 01背包问题

1.1 朴素做法

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//01背包问题 朴素做法
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m;
int v[N],w[N];//v是体积,w是权重
int f[N][N];//f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
   for(int i=1;i<=n;i++)
      for(int j=0;j<=m;j++)
     {
      f[i][j]=f[i-1][j];//因为左边一定是存在的
      if(j>=v[i])//假如背包容量还有
        f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);//取两边的最大
     }
   cout<<f[n][m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

 1.2 利用滚动数组优化做法 把第一维删除即可

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//01背包问题 滚动数组优化法
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m;
int v[N],w[N];//v是体积,w是权重
int f[N];//优化为一维,f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
   for(int i=1;i<=n;i++)
      for(int j=m;j>=v[i];j--)//如果要用到上一维i-1的话,则从最大开始枚举
        f[j]=max(f[j],f[j-v[i]]+w[i]);//取两边的最大
   cout<<f[m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

2 完全背包问题 

2.1 朴素做法 时间复杂度高,一般会TLE

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//完全背包问题,朴素做法
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m;
int v[N],w[N];//v是体积,w是权重
int f[N][N];//f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
   for(int i=1;i<=n;i++)
      for(int j=0;j<=m;j++)
        for(int k=0;k*v[i]<=j;k++)//选k个
         f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);//取两边的最大
   cout<<f[n][m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

2.2 推理优化,降低时间复杂度

 

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//完全背包问题,优化后算法
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m;
int v[N],w[N];//v是体积,w是权重
int f[N][N];//f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
   for(int i=1;i<=n;i++)
      for(int j=0;j<=m;j++)
       {
        f[i][j]=f[i-1][j];//右边肯定够装的,直接等于
        if(j>=v[i])//假如背包还能装的下
            f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);//取两边的最大
       }
   cout<<f[n][m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

2.3 滚动数组继续优化 把第一维删除即可

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//完全背包问题,滚动数组继续优化
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m;
int v[N],w[N];//v是体积,w是权重
int f[N];//优化为一维,f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
   for(int i=1;i<=n;i++)
      for(int j=v[i];j<=m;j++)//如果只用到本维i的话,则从小的开始枚举
            f[j]=max(f[j],f[j-v[i]]+w[i]);//取两边的最大
   cout<<f[m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

3 多重背包问题  

3.1 朴素做法与完全背包相同,只是多了个限定k<=s,而且很容易TLE

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//多重背包问题 朴素做法与完全背包相同,多了个数量限制
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int v[N],w[N],s[N];//v是体积,w是权重,s是数量个数
int f[N][N];//f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
   for(int i=1;i<=n;i++)
      for(int j=0;j<=m;j++)
        for(int k=0;k<=s[i]&&k*v[i]<=j;k++)//枚举可供选择的数
            f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+k*w[i]);//取两边的最大
   cout<<f[n][m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

3.2 优化做法,优化成01背包问题,降低时间复杂度

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//多重背包问题 优化做法,优化成01背包问题
#include<bits/stdc++.h>
using namespace std;
const int N=25000;//因为每个数有2000个,而每个可以拆分为log2000=12个,所以得开24000个01背包,这样就拆分成选和不选的01背包了
int n,m;
int v[N],w[N];//v是体积,w是权重
int f[N];//01背包的优化 f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   int cnt=0;//当前背包数
   for(int i=1;i<=n;i++)
   {
       int a,b,s;
       cin>>a>>b>>s;
       int k=1;
       while(k<=s)//下面进行拆分背包,按照二进制进行拆分背包
       {
           cnt++;//背包数加一
           v[cnt]=a*k;//当前背包的体积
           w[cnt]=b*k;//当前背包的权重
           s-=k;//当前的个数减少
           k*=2;//增加两倍
       }
       if(s>0)//假如还有剩余
       {
           cnt++;//背包数加一
           v[cnt]=a*s;//最后拆分背包的体积
           w[cnt]=b*s;//最后拆分背包的权重
       }
   }
  n=cnt;//新的背包个数,也就是拆分后的个数
  //下面就是01背包的做法
   for(int i=1;i<=n;i++)
      for(int j=m;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);//取两边的最大
   cout<<f[m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

4 分组背包问题

 

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//分组背包问题 
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int v[N][N],w[N][N],s[N];//v是体积,w是权重,s是数量
int f[N];//f是分析的f(i,j),表示前i个数选权重不超过j的最大值
int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++)
   {
       cin>>s[i];//读入第i个数的数量
       for(int j=0;j<s[i];j++)//读入第i个数的所有物品的体积和权重
         cin>>v[i][j]>>w[i][j];
   }
    for(int i=1;i<=n;i++)//根据状态计算来求
        for(int j=m;j>=0;j--)
         for(int k=0;k<s[i];k++)
           if(v[i][k]<=j)//假如背包还有位置
              f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);//取两边的最大
   cout<<f[m]<<endl;//输出结果
    return 0;
}
/*到达胜利之前无法回头*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值