动态规划1----背包问题

动态规划是一个非常难啃的问题,模型就非常的多,也没有什么很固定的模板。动态规划的模型有:背包问题 线性dp 区间dp 计数类dp 数位统计类dp 状态压缩类dp 树形dp 记忆化搜索。

其中背包问题的模型就非常之多,这篇文章记录下背包的几大类问题。

一:01背包

01背包的问题模型,问题如下:

以上这是01背包的问题,我们思考的方式采用y总的分析法。即从状态表示和状态计算上看。

以上的分析法是y总的分析套路,代码如下:

//二维
#include<bits/stdc++.h>
using namespace std;

int w[1005];//存放每一个价值
int v[1005];//存放每一个物品的体积
int f[1005][1005];//存放每个状态值

int main()
{
    int n,m;
    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];//不含i的时候
         if(j>=v[i])  f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);//必须前面装过的还可以至少有v[i]的地方可以装,所以要判断,否则也会出现负数的情况。
     }
     
    cout<<f[n][m]<<endl;
    return 0;
}



//优化成一维,优化成一维不好理解,不如二维简单明了
#include<bits/stdc++.h>
using namespace std;

int w[1005];
int v[1005];
int f[1005];

int main()
{
    int n,m;
    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--)
     {
         f[j]=max(f[j],f[j-v[i]]+w[i]);
     }
    cout<<f[m]<<endl;
    return 0;
}

二:完全背包问题

完全背包问题指的是每一件物品都可以无限选择,只要体积不超过背包的总体积。

场景题目:

 同样的,我们采用了y总的分析方法。从状态表示和状态计算两个方面去分析这道题目。

#include<bits/stdc++.h>
using namespace std;

int v[1005];
int w[1005];
int f[1005][1005];

int main()
{
    int n,m;
    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++)
        {
          f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+w[i]*k);//表达式
        }
   cout<<f[n][m];
   return 0;
    
}



//优化代码,减少一层循环
#include<bits/stdc++.h>
using namespace std;

int v[1005];
int w[1005];
int f[1005][1005];

int main()
{
    int n,m;
    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=1;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];
   return 0;
    
}


//完全背包的终极写法,用一维来解决问题
#include<bits/stdc++.h>
using namespace std;

int v[1005];
int w[1005];
int f[1005];

int main()
{
    int n,m;
    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++)
      {
          f[j]=max(f[j],f[j-v[i]]+w[i]);
      }
   cout<<f[m];
   return 0;
    
}

 三:多重背包问题

多重背包问题是指限制第i个物品的个数而不能无限制的取,我们在分析的时候依然用yxc的dp分析法。

因此,其实这个多重背包问题的朴素版写法和完全背包是差不多的,只是我们限制取的件数

代码:

//多重背包的朴素版写法
#include<bits/stdc++.h>
using namespace std;

int w[105];
int v[105];
int s[105];
int f[105][105];

int main()
{
    int n,m;
    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*v[i]<=j&&k<=s[i];k++)//给定双重限制,这个代码只比完全背包多一点代码
        {
            f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
        }
    cout<<f[n][m]<<endl;
    return 0;
}

四.多重背包的优化

因为在优化之前,多重背包的时间复杂n*m*s三重循环,当数据太大时就要优化,这个优化我暂时看不太懂,但是时间复杂度是n*m*logs,我先把题目和代码贴出来,后期再遇到题目的时候在回过来看。

 代码如下:

#include<bits/stdc++.h>
using namespace std;

int n,m;
int v[25000];
int w[25000];
int f[25000];
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;
    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];
    return 0;
}

五:分组背包问题

先看分组背包问题的情形。

 很明显从题目上看,这是以组为单位进行计算的,其实可以考虑成以组为单位的01背包问题,我们用最常用的思路建立模型。

代码如下:

#include<bits/stdc++.h>
using namespace std;

int n,m;
int v[105][105];
int w[105][105];
int s[105];
int f[105][105];

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        for(int j=0;j<s[i];j++)
           cin>>v[i][j]>>w[i][j];
    }
    for(int i=1;i<=n;i++)
      for(int j=0;j<=m;j++)
      {
          f[i][j]=f[i-1][j];//不选第i个的时候
          for(int k=0;k<=s[i];k++)//选第i个时候要选那一个要更新出来
           if(v[i][k]<=j)  f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
      }
    cout<<f[n][m];
    return 0;
}

 背包问题暂时结束!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值