C++动态规划中关于背包问题讲解

 更新时间:2023年03月15日 10:03:39   作者:清风何渡  
可能有些读者有接触过动态规划,可能也有一些读者以前完全不知道动态规划这个东西,别担心,我这篇文章会为读者做一个入门,好让读者掌握这个重要的知识点

一、分割等和子集-最后一块石头的重量II

背包问题,难点往往在第一步:dp数组表示什么

分割等和子集问题,较好的方式是:求装满背包后最大重量是多少(有点绕哈哈)

这是个题型:对于判断能不能恰好装满背包的问题,用dp表示重量,判断是否最终的dp[m]==m

bool canPartition(int* nums, int numsSize){
    //首先数组元素求和的sum,若sum%2==1,返回false
    //若sum%2==0,定义m=sum/2,n=numsSize
    //则问题变成了能否装满容量为m的背包
    //进一步变成了求装满容量为m的背包得到的最大价值量(本题价值量即为重量)
    //1.dp[j]表示装满容量为j的背包能获得的最大价值量
    //2.递推式:dp[j]=fmax(dp[j],dp[j-nums[i]]+nums[i]);
    //3.dp数组初始化:dp[i]=0;
    //4.遍历顺序:0-1背包顺序(滚动数组)
    int sum=0;
    for(int i=0;i<numsSize;i++) sum+=nums[i];
    if(sum%2==1) return false;
    int m=sum/2,n=numsSize;
    int dp[m+1];
    for(int j=0;j<=m;j++) dp[j]=0;
    for(int i=0;i<n;i++){
        for(int j=m;j>=nums[i];j--)
            dp[j]=fmax(dp[j],dp[j-nums[i]]+nums[i]);
    }
    if(dp[m]==m) return true;
    else return false;
}

二、目标和

求组合数模板:dp[0]=1;dp[j]+=dp[j-nums[i]];

int findTargetSumWays(int* nums, int numsSize, int target){
    //首先数组元素求和的sum,若满足题意,m+(m-target)=sum
    //若(sum+target)%2==1,返回0;
    //若sum<abs(target),返回0;
    //否则,有m=(sum+target)/2;
    //问题就变成了整数m可以有多少表达式表示出
    //进一步变成了求装满容量为m的背包的最大组合数
    //1.dp[j]表示装满容量为j的背包的最大表达式的组合数
    //2.递推式:
    //组合问题模板:dp[0]=1;dp[j]+=dp[j-nums[i]];
    //3.dp数组初始化:dp[i]=0;dp[0]=1;
    int sum=0;
    for(int i=0;i<numsSize;i++) sum+=nums[i];
    if(sum<abs(target)||(sum+target)%2==1) return 0;
    int m=(sum+target)/2,n=numsSize;
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int i=0;i<n;i++){
        for(int j=m;j>=nums[i];j--)
            dp[j]+=dp[j-nums[i]];
    }
    return dp[m];
}

三、一和零

注意二维滚动数组不能写在同一个for循环中,这题背一下

int findMaxForm(char ** strs, int strsSize, int m, int n){
    //本题是二维背包,不过是比一维多了一步而已
    //1.dp[i][j]表示背包容量为i个0、j个1时,最多能装的物品个数
    //2.递推式:
    //dp[i][j]=fmax(dp[i][j],dp[i-cnt0][j-cnt1]+1);
    //3.dp数组初始化:
    //dp[i][j]=0;
    //4.遍历顺序:二维滚动数组(注意不能把i和j写在同一个for循环中)
    int dp[m+1][n+1];
    for(int i=0;i<=m;i++){
        for(int j=0;j<=n;j++)
            dp[i][j]=0;
    }
    for(int k=0;k<strsSize;k++){
        int cnt0=0,cnt1=0;
        int len=strlen(strs[k]);
        for(int i=0;i<len;i++){
            if(strs[k][i]=='0') cnt0++;
            else cnt1++;
        }
        for(int i=m;i>=cnt0;i--){
            for(int j=n;j>=cnt1;j--){
                dp[i][j]=fmax(dp[i][j],dp[i-cnt0][j-cnt1]+1);
            }
        }
    }
    return dp[m][n];
}

四、零钱兑换II

多重背包和0-1背包唯一的区别在遍历顺序

我们知道01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次。

而完全背包的物品是可以添加多次的,所以要从小到大去遍历

int change(int amount, int* coins, int coinsSize){
    int m=amount,n=coinsSize;
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int i=0;i<n;i++){
        for(int j=coins[i];j<=m;j++)
            dp[j]+=dp[j-coins[i]];
    }
    return dp[m];
}

五、排列与组合

组合总数IV(排列问题)

本题要求的是排列数(即考虑排列顺序)

求排列数,外层遍历重量,内层遍历物品,且均为从左到右遍历

int combinationSum4(int *nums,int n,int m){
    //1.dp[j]表示背包容量为j时,有多少种方法能使背包被装满“
    //2.递推式:
    //dp[j]+=dp[j-nums[i]];
    //3.初始化:
    //dp[i]=0;dp[0]=1;
    //4.遍历顺序:
    //本题要求的是排列数(即考虑排列顺序)
    //求排列数,外层遍历重量,内层遍历物品,且均为从左到右遍历
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int j=0;j<=m;j++){
        for(int i=0;i<n;i++){
            if(j>=nums[i]&&dp[j]<INT_MAX-dp[j-nums[i]])
                dp[j]+=dp[j-nums[i]];
        }
    }
    return dp[m];
}

零钱兑换(组合问题)

本题要求的是组合数(即不考虑排列顺序)

求组合数,外层遍历物品,内层遍历重量,且均为从左到右遍历

int int coinChange(int* coins, int coinsSize, int amount){
    //1.dp[j]表示背包容量为j时,有多少种方法能使背包被装满“
    //2.递推式:
    //dp[j]+=dp[j-coins[i]];
    //3.初始化:
    //dp[i]=0;dp[0]=1;
    //4.遍历顺序:
    //本题要求的是组合数(即不考虑排列顺序)
    //求组合数,外层遍历物品,内层遍历重量,且均为从左到右遍历
    int m=amount,n=coinsSize;
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int i=0;i<n;i++){
        for(int j=coins[i];j<=m;j++)
            dp[j]+=dp[j-coins[i]];
    }
    return dp[m];
}

到此这篇关于C++动态规划中关于背包问题讲解的文章就介绍到这了,更多相关C++动态规划背包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现的O(n)复杂度内查找第K大数算法示例

    C++实现的O(n)复杂度内查找第K大数算法示例

    这篇文章主要介绍了C++实现的O(n)复杂度内查找第K大数算法,结合实例形式分析了算法的原理以及具体实现方法,需要的朋友可以参考下
    2017-08-08
  • C++的缺省参数你了解嘛

    C++的缺省参数你了解嘛

    这篇文章主要为大家介绍了C++缺省参数,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • C++中图片类型的识别与转换详解方法

    C++中图片类型的识别与转换详解方法

    本文简单的介绍一下C++语言中如何识别图片文件的类型,以及各图片类型之间的转换方法,并提供相关的源码供大家参考,感兴趣的朋友快来看看吧
    2021-11-11
  • C语言 冒泡排序算法详解及实例

    C语言 冒泡排序算法详解及实例

    这篇文章主要介绍了C语言 冒泡排序算法详解及实例的相关资料,需要的朋友可以参考下
    2016-11-11
  • C语言预处理器使用方法讲解

    C语言预处理器使用方法讲解

    C预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把C预处理器(C Preprocessor)简写为CPP
    2022-12-12
  • c语言读取csv文件和c++读取csv文件示例分享

    c语言读取csv文件和c++读取csv文件示例分享

    这篇文章主要介绍了c语言读取csv文件和c++读取csv文件示例,需要的朋友可以参考下
    2014-03-03
  • 浅谈 C++17 里的 Visitor 模式

    浅谈 C++17 里的 Visitor 模式

    Visitor模式经常用于将更新的设计封装在一个类中,并且由待更改的类提供一个接受接口,其关键技术在于双分派技术,本文主要介绍 C++17 里的 Visitor 模式的相关资料,需要的朋友可以参考下面文章的具体内容
    2021-09-09
  • typedef和#define用法区别总结

    typedef和#define用法区别总结

    在C还是C++代码中,typedef都使用的很多,在C代码中尤其多,typedef与#define有些相似,其实是不同的,特别是在一些复杂的用法上,下面这篇文章主要给大家介绍了关于typedef和#define用法区别总结的相关资料,需要的朋友可以参考下
    2023-06-06
  • C++联合体union用法实例详解

    C++联合体union用法实例详解

    这篇文章主要介绍了C++联合体union用法,较为详细的分析了C++中联合体的概念、实用技巧及相关注意事项,需要的朋友可以参考下
    2015-05-05
  • C++利用libcurl库实现多线程文件下载

    C++利用libcurl库实现多线程文件下载

    这篇文章主要为大家详细介绍了C++如何利用libcurl库实现多线程文件下载,文章的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考下
    2024-01-01

最新评论