首页 > 其他分享 >动态规划<八> 完全背包问题及其余背包问题

动态规划<八> 完全背包问题及其余背包问题

时间:2025-01-04 22:33:52浏览次数:7  
标签:初始化 背包 int 问题 amount vector 动态 dp

目录

例题引入---找到解决问题模版

LeetCode 经典OJ题

1.第一题

 2.第二题

 3.第三题

 其余的一些背包问题

1.二维费用的背包问题

1.第一题

2.第二题

2.其余杂题

例题引入---找到解决问题模版

OJ 传送门 牛客 DP42 【模板】完全背包

画图分析:

 使用动态规划解决(第二问与第一问的不同之处用绿色来标记)

1.状态表示

dp[i][j]表示从前i个物品中挑选,总体积不超过j的所有选法中,所挑选出来的最大价值

dp[i][j]表示从前i个物品中挑选,总体积等于j的所有选法中,所挑选出来的最大价值

2.状态转移方程

3.初始化  根据01背包问题得知,我们只需初始化第一行即可

4.填表顺序   从上往下填每一行,每一行从左往右

5.返回值  对于第一问的是直接返回dp[n][V]

第二问则是需要判断下dp[n][V]==-1?0:dp[n][V] 

具体代码   ---优化前

#include <iostream>
#include <string.h>
using namespace std;
const int N=1010;
int n,V;
int w[N],v[N],dp[N][N];
int main()
{
    //读入数据
    cin>>n>>V;
    for(int i=1;i<=n;++i)
      cin>>v[i]>>w[i];

    //解决第一问
    for(int i=1;i<=n;++i)
      for(int j=0;j<=V;++j)
      {
        dp[i][j]=dp[i-1][j];
        if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
      }
    cout<<dp[n][V]<<endl;

    //解决第二问
    memset(dp,0,sizeof dp);
    for(int j=1;j<=V;++j) dp[0][j]=-1;
    for(int i=1;i<=n;++i)
      for(int j=0;j<=V;++j)
      {
        dp[i][j]=dp[i-1][j];
        if(j>=v[i] && dp[i][j-v[i]]!=-1) 
        dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
      }
    cout<<(dp[n][V]==-1? 0:dp[n][V]);
    return 0;
}

做优化

优化后的代码

#include <iostream>
#include <string.h>
using namespace std;
const int N=1010;
int n,V;
int w[N],v[N],dp[N];
int main()
{
    //读入数据
    cin>>n>>V;
    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<=V;++j)
      {
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
      }
    cout<<dp[V]<<endl;

    //解决第二问
    memset(dp,0,sizeof dp);
    for(int j=1;j<=V;++j) dp[j]=-1;
    for(int i=1;i<=n;++i)
      for(int j=v[i];j<=V;++j)
      {
        if(dp[j-v[i]]!=-1) 
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
      }
    cout<<(dp[V]==-1? 0:dp[V]);
    return 0;
}

LeetCode 经典OJ题

1.第一题

OJ 传送门 LeetCode<322> 零钱兑换

画图分析:

 使用动态规划解决

1.状态表示

dp[i][j]表示从前i个硬币中挑选,总和正好等于j,所有的选法中,最少的硬币数

2.状态转移方程

3.初始化

4.填表顺序  从上往下每一行,每一行从左往右

5.返回值   dp[n][amount]>=INF? -1:dp[][amount]

具体代码

 int coinChange(vector<int>& coins, int amount) 
    {
        const const int INF=0x3f3f3f3f;
        int n=coins.size();

        vector<vector<int>> dp(n+1,vector<int>(amount+1));
        for(int j=1;j<=amount;++j) dp[0][j]=INF;
        for(int i=1;i<=n;++i)
         for(int j=0;j<=amount;++j)
         {
            dp[i][j]=dp[i-1][j];
            if(j>=coins[i-1]) dp[i][j]=min(dp[i][j],dp[i][j-coins[i-1]]+1);
         }
        
        return dp[n][amount]>=INF? -1:dp[n][amount];
    }


//优化后
 int coinChange(vector<int>& coins, int amount) 
    {
        const const int INF=0x3f3f3f3f;
        int n=coins.size();

        vector<int>dp(amount+1,INF);
        dp[0]=0;
        for(int i=1;i<=n;++i)
         for(int j=coins[i-1];j<=amount;++j)
            dp[j]=min(dp[j],dp[j-coins[i-1]]+1);
        
        return dp[amount]>=INF? -1:dp[amount];
    }

 2.第二题

OJ 传送门 LeetCode<518> 零钱兑换II

画图分析:

 使用动态规划解决

1.状态表示

dp[i][j]表示从前i个硬币中挑选,总和正好等于j,一共有多少种选法

2.状态转移方程

3.初始化 初始化第一行,第一个位置前0个凑出0元,即什么都不选,是一种方法,初始化为1,其余位置是凑不出来的,即初始化为0

4.填表顺序   从上往下每一行,每一行从左往右

5.返回值   dp[n][amount]

具体代码 

int change(int amount, vector<int>& coins) 
    {
        vector<unsigned int> dp(amount + 1); 
        dp[0] = 1; 
        for(int i=1;i<=coins.size();++i) 
         for(int j = coins[i-1]; j <= amount; j++) 
           dp[j] += dp[j - coins[i-1]];
        return dp[amount];
    }

 3.第三题

OJ 传送门 LeetCode<279> 完全平方数

 画图分析:

对于本题可以从左往右进行挑选完全平方数,来合成数n,且每个完全平方数都能重复进行选择

这就是完全背包问题 使用动态规划解决

1.状态表示

dp[i][j]表示从前i个完全平方数中进行挑选,总和恰好为j,完全平方数的数量

2.状态转移方程

3.初始化  初始化第一行,第一个位置初始化为0,其余位置的状态是不存在的,因为题目求的是min,为不影响填表,初始化为一个较大值ox3f3f3f3f

4.填表顺序  从上往下,从左往右

5.返回值 dp[sqrt(n)][n]

具体代码:

int numSquares(int n) 
    {
        int m=sqrt(n);
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int j=1;j<=n;++j) dp[0][j]=0x3f3f3f3f;
        for(int i=1;i<=m;++i)
            for(int j=0;j<=n;++j)
            {
                dp[i][j]=dp[i-1][j];
                if(j>=i*i) dp[i][j]=min(dp[i][j],dp[i][j-i*i]+1);
            }
        return dp[m][n];
    }

//优化后
 int numSquares(int n) 
    {
        int m=sqrt(n);
        vector<int>dp(n+1,0x3f3f3f3f);
        dp[0]=0;
        for(int i=1;i<=m;++i)
            for(int j=i*i;j<=n;++j)
            {
                dp[j]=min(dp[j],dp[j-i*i]+1);
            }
        return dp[n];
    }

 其余的一些背包问题

1.二维费用的背包问题

二位费用的背包问题简单来说就是指有两个限定条件的背包问题

1.第一题

OJ 传送门 LeetCode<474>一和零

画图分析

 本题目简单来说,就是从数组中挑选字符串,挑出来的字符串要共同满足1和0 的最多出现次数的条件,因此是一个背包问题,且要满足两个条件,因此是二维费用的背包问题

使用动态规划解决

1.状态表示

dp[i][j][k]表示从前i个字符串中挑选,字符0的个数不超过j,字符1的个数不超过k,所有的选法中最大的长度

2.状态转移方程

3.初始化  对于三维的也一样,仅需初始化关于i=0的这些数据,根据状态表示,都初始化为1

4.填表顺序    i从小到大

5.返回值       dp[len][m][n]

具体代码

int findMaxForm(vector<string>& strs, int m, int n) 
    {
        int len=strs.size();
        vector<vector<vector<int>>>dp(len+1,vector<vector<int>>(m+1,vector<int>(n+1)));
        for(int i=1;i<=len;++i)
        {
            //统计字符串中0 1的个数
            int a=0,b=0;
            for(auto ch:strs[i-1])
            {
                if(ch=='0') ++a;
                else ++b;
            }
            for(int j=0;j<=m;++j)
            {
                for(int k=0;k<=n;++k)
                {
                    dp[i][j][k]=dp[i-1][j][k];
                    if(j>=a && k>=b) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-a][k-b]+1);
                }
            }
        }
        return dp[len][m][n];
    }

//优化
int findMaxForm(vector<string>& strs, int m, int n) 
    {
        int len=strs.size();
        vector<vector<int>>dp(m+1,vector<int>(n+1));
        for(int i=1;i<=len;++i)
        {
            //统计字符串中0 1的个数
            int a=0,b=0;
            for(auto ch:strs[i-1])
            {
                if(ch=='0') ++a;
                else ++b;
            }
            for(int j=m;j>=a;--j)//从大到小
            {
                for(int k=n;k>=b;--k)
                {
                    dp[j][k]=max(dp[j][k],dp[j-a][k-b]+1);
                }
            }
        }
        return dp[m][n];
    }
2.第二题

OJ传送门 LeetCode<879>盈利计划 

画图分析:

会发现这个问题也是从这个表中选取对应的数据,来满足一定条件的需求,且每个位置只有选与不选的情况,满足两个条件,因此是二维费用的01背包问题

1.状态表示

dp[i][j][k]表示在前i个工作中挑选,总人数不超过j,总利润至少为k,一共有多少中选法

2.状态转移方程

3.初始化   初始化情况为从前0中挑选,此时不管怎么挑利润都是0,但挑选出来的空集也是一种方法,即初始化为1  dp[0][j][0]=1(0<=j<=n)

4.填表顺序     i从小到大

5.返回值      dp[len][n][m]

 具体代码:

int profitableSchemes(int n, int m, vector<int>& g, vector<int>& p) 
    {
        const int MOD=1e9+7;
        int len=g.size();
        vector<vector<vector<int>>> dp(len+1,vector<vector<int>>(n+1,vector<int>(m+1)));
        for(int j=0;j<=n;++j) dp[0][j][0]=1;
        for(int i=1;i<=len;++i)
        {
            for(int j=0;j<=n;++j)
            {
                for(int k=0;k<=m;++k)
                {
                    dp[i][j][k]=dp[i-1][j][k];
                    if(j>=g[i-1])
                     dp[i][j][k]+=dp[i-1][j-g[i-1]][max(0,k-p[i-1])];
                    dp[i][j][k]%=MOD;
                }
            }
        }
        return dp[len][n][m];
    }

//优化后
 int profitableSchemes(int n, int m, vector<int>& g, vector<int>& p) 
    {
        const int MOD=1e9+7;
        int len=g.size();
       vector<vector<int>>dp(n+1,vector<int>(m+1));
        for(int j=0;j<=n;++j) dp[j][0]=1;
        for(int i=1;i<=len;++i)
        {
            for(int j=n;j>=g[i-1];--j)
            {
                for(int k=m;k>=0;--k)
                {
                    
                    dp[j][k]+=dp[j-g[i-1]][max(0,k-p[i-1])];
                    dp[j][k]%=MOD;
                }
            }
        }
        return dp[n][m];
    }

2.其余杂题

OJ传送门 LeetCode<377>组合总数IV

 画图分析:

使用动态规划解决

既然不能使用背包问题的解决方法的话,那就使用根据分析问题的过程中,发现重复子问题,抽象出来一个状态表示

初始化 秩序初始化dp[0]即可,根据状态表示,初始化为1

填表顺序   从左往右

返回值   dp[target]

具体代码:

int combinationSum4(vector<int>& nums, int target) 
    {
        vector<double> dp(target+1);
        dp[0]=1;
        for(int i=1;i<=target;++i)
            for(auto x:nums)
            {
                if(i>=x) dp[i]+=dp[i-x];
            }
        return dp[target];
    }

 2.第二题

OJ传送门 LeetCode<96>不同的二叉搜索树

画图分析:

使用动态规划解决

1.状态表示 根据分析问题的过程中,发现重复子问题,抽象出来一个状态表示

3.初始化   根据状态表示初始化dp[0](空树)为1即可

4.填表顺序    从左往右

5.返回值    dp[n]

具体代码:

int numTrees(int n) 
    {
        vector<int> dp(n+1);
        dp[0]=1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=i;++j)
                dp[i]+=dp[j-1]*dp[i-j];
        return dp[n];
    }

标签:初始化,背包,int,问题,amount,vector,动态,dp
From: https://blog.csdn.net/Miwll/article/details/144910654

相关文章

  • 《约瑟夫问题 循环链表》
    约瑟夫问题循环链表题解来了!!!#include<bits/stdc++.h>usingnamespacestd;intm,n;structNode{ intdata; Node*next;}*head,*p,*tail,*temp;intmain(){ cin>>m>>n; head=newNode; head->next=NULL; tail=head; for(inti=1;i&......
  • MySQL中深度分页问题的优化
    MySQL中深度分页问题的优化在MySQL中,使用LIMIT子句进行分页查询时,可能会遇到一个常见的性能问题:当LIMIT子句中的偏移量X很大时,查询速度会显著下降。例如,LIMIT0,10可能只需要20毫秒,而LIMIT1000000,10可能需要15秒或更长时间。这个问题被称为深度分页问题。下面我们来深入......
  • 跟着问题学3.3——Faster R-CNN详解及代码实战(1)
    FastR-CNN的不足选取区域使用的算法是固定的,不参与学习选取区域的算法本身消耗比较高(搜索选择法)选取区域的算法选出来的区域大部分都是重合的,并且只有很小一部分包含我们想要识别的对象区域范围的精度比较低(即使经过调整)判断分类有时只能使用部分包含对象的区域(例如......
  • 请问数据库迁移后无法登录的问题及解决方案
    数据库迁移后无法登录的问题可能由多种原因引起,特别是在迁移过程中涉及到数据库结构、权限设置或配置文件的变化。为了确保顺利迁移并解决登录问题,您可以按照以下步骤进行排查和处理:确认迁移完整性: 首先,确保数据库迁移过程完整无误。检查迁移工具的日志,确认所有表、视图、存储......
  • 解决服务器内部 PHP 登录失败的问题
    用户在尝试通过PHP脚本登录服务器时遇到了失败的情况,尽管使用了最高权限账户。这种情况可能会影响到服务器管理任务的执行,需要尽快解决。解决方案:验证凭据准确性:首先确认使用的用户名和密码是最新且准确的。即使是最高权限账户,也有可能因为最近更改过密码而导致登录失败。仔......
  • 处理服务器无法连接及网站打不开的问题
    用户报告称其服务器无法通过远程桌面连接,并且网站也无法正常访问。这种情况可能由多种因素引起,包括但不限于网络连接问题、服务器配置错误或硬件故障等。解决方案:检查网络连通性:使用ping、tracert等命令行工具测试本地计算机与服务器之间的网络连通性。观察是否有明显的网络中......
  • 处理织梦后台栏目管理不显示内容及报错问题的方法
    用户报告称其织梦(DedeCMS)后台栏目管理页面不显示任何内容,点击子栏目还会报错。这种情况严重影响了网站内容的管理和更新工作,需要找出根本原因并解决。解决方案:检查数据库连接:确保织梦能够正确连接到数据库。检查应用程序配置文件(如PHP脚本中的config.php),确保数据库连接参数(如......
  • 如何提升网站访问速度并解决晚上打不开的问题?
    当您的网站在晚上访问特别慢甚至打不开时,可能是由于多种因素导致的。为了提升网站访问速度并解决晚上打不开的问题,您可以采取以下措施:检查服务器资源使用情况:首先,登录到您的服务器管理面板(如宝塔面板),查看CPU、内存和磁盘I/O的使用情况。如果发现资源占用过高,可能是由于流量高峰......
  • 如何解决FTP连接失败的问题
    针对您提到的FTP连接失败问题,我们可以按照以下步骤进行排查和修复:验证FTP账户信息: 首先,请确认您使用的FTP账户名和密码是否正确无误。由于FTP连接依赖于正确的认证信息,任何输入错误都可能导致连接失败。建议您再次核对提供的FTP账号、密码以及上传地址,确保所有信息准确无误。......
  • 如何解决云服务器系统盘升级后容量未变化的问题
    系统盘升级后容量未变化的问题。为了帮助您解决这个问题,请按照以下步骤进行排查和处理:一、确认系统盘升级状态检查订单状态确认系统盘升级订单是否已完成。可以在云服务平台控制面板中查看订单状态,确保订单已成功完成。如果订单未完成,联系客服确认原因。检查磁盘分区使用命......