背包问题


声明:以下所有题目套路以及部分题目代码均来自于labuladong的算法小抄

01背包

套路:
初始化
for(状态1:状态)
for(状态2:状态)
{
if(背包装不下)
dp=
else
{
dp=f(选择)(求最值就用max求方法数就用+)
}
}

leetcode494 目标和(变体01背包)

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/target-sum

  • 易错点:在遍历dp数组时要分成背包装的下与装不下两种情况
  • 难点:将+ -两种运算变成只有+一种运算(数学方法得出+的次数,不装入+就是-
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //dp[N][W] 前N个数目标数为W的方法数
        //选择:装入 不装入
        //状态,数组,目标数
        //初始化dp[0][W]=0;dp[0][0]=1;
                int sum = 0;
        for (int i = 0; i < nums.size(); i++) sum += nums[i];
        if (target > sum) return 0; // 此时没有方案
        if ((target + sum) % 2 == 1) return 0; // 此时没有方案
        int bagSize = (target + sum) / 2;

        vector<vector<int>> dp(nums.size()+1,vector<int> (bagSize+1));

        for(int i=0;i<bagSize+1;i++)
        {
            dp[0][i]=0;
        }
        dp[0][0]=1;
        for(int i=1;i<=nums.size();i++)
        {
            for(int j=0;j<=bagSize;j++)
            {
                if(j-nums[i-1]<0)//if(j-nums[i]<0)!!!!!总是错//背包装不下
                dp[i][j]=dp[i-1][j];
                else
                dp[i][j]=dp[i-1][j-nums[i-1]]+dp[i-1][j];//背包装得下
            }
        }
        return dp[nums.size()][bagSize];

    }
};

子集背包

leetcode416

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        //dp[N][W]转化为可装载重量为sum/2的背包和N个物品,每个物品重量为sum[i],是否存在一种装法把背包装满。
        //初始化dp[0][..]=false dp[...][0]=true.
        //选择:装与不装。状态:剩余容量减少,剩余容量不变,
        int sum=0;
        for(int num:nums) sum=sum+num;
        if(sum%2!=0) return false;//如果sum是奇数,直接pass
        sum=sum/2;
       bool **dp=new bool* [nums.size()+1];
       for (int i=0;i<=nums.size();i++)
       {
           dp[i]=new bool[sum+1];
           for(int j=0;j<=sum;j++)
           {
               dp[i][j]=false;
               dp[i][0]=true;
           }
       }
       for(int i=1;i<=nums.size();i++)
       {
           for(int j=1;j<=sum;j++)
           {
               if(j-nums[i-1]<0)//背包容量不足,不能装入物品,维持i-1个物品时的判断
               dp[i][j]=dp[i-1][j];
               else
               {
                   dp[i][j]=dp[i-1][j]|dp[i-1][j-nums[i-1]];//选择装入或者不装入
               }
           }
       }

        return dp[nums.size()][sum];

    }
};

完全背包

leetcode518 零钱兑换2

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

  • 易错点 dp[i][j]=dp[i][j-coins[i-1]]+dp[i-1][j];注意在01背包里dp[i][j]=dp[i-1][j-nums[i-1]]+dp[i-1][j]
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        //dp[i][j]对于前i个物品,背包容量为j时,有dp[i][j]种方法可以装满背包
        //选择:放入背包or不放入背包
        //状态 可选择的物品,背包容量
        //初始化 j=0时dp[i][0]=1,这我真没法理解,凑零元一个硬币都不加也能算一种方案吗
        //dp[0][j]=0; 
        //状态转移:放入背包dp[i][j]=dp[i][j-coins[i-1]]//这里注意:为什么时dp[i][j-coins[i-1]
        //而非dp[i-1]][j-coins[i-1]]呢,因为这是个无限硬币的组合类问题,所以可以一直装下去直到选择不装。。
        //         不放入背包dp[i][j]=dp[i-1][j]
        vector<vector<int>> dp(coins.size()+1,vector<int>(amount+1,0));
        for(int i=0;i<=coins.size();i++)//注意赋值全面
        dp[i][0]=1;//初始化
        
        for(int i=1;i<=coins.size();i++)
        {
            for(int j=1;j<=amount;j++)
            {
                if(j-coins[i-1]>=0)//背包装得下
                dp[i][j]=dp[i][j-coins[i-1]]+dp[i-1][j];
                else//背包装不下
                dp[i][j]=dp[i-1][j];



            }
        }
        return dp[coins.size()][amount];


    }
};

leetcode279 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/perfect-squares

  • 最少数量题,大部分初始化为INT—MAX,状态转移时加一
class Solution {
public:
    int numSquares(int n) {
        vector<int> num;
        for(int i=1;i*i<=n;i++)
        {
            
            num.push_back(i*i);
            if(i*i>=n)
            break;

        }
        
        //dp[i][j]前i个数的背包容量j 所需要最少数量
        //dp[i][0]=0; dp[0][j]=int_max
        //dp[i][j]=min(dp[i-1][j],dp[i][j-num[i]])
        vector<vector<int>> dp(num.size()+1,vector<int>(n+1,INT_MAX));
      for(int i=0;i*i<=n;i++)
        {
            dp[i][0]=0;
  
        }
       for(int i=1;i<=num.size();i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(j-i*i>=0&&dp[i][j-num[i-1]]!=INT_MAX)//易错点:dp[i][j-num[i-1]]!=INT_MAX的判断
               dp[i][j]=min(dp[i-1][j],dp[i][j-num[i-1]]+1);//易错点:num[i-1]别写成num[i]!!!!!!!!!!!!!!!!还有记得加一
               else
                dp[i][j]=dp[i-1][j];


            }
        }
        return dp[num.size()][n];

    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值