【动态规划DP】最大连续字段和+背包问题

刷题记录 专栏收录该内容
16 篇文章 0 订阅

1.Leetcode 53

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

class Solution {
public:
    int maxSubArray(vector<int>& nums) 
    {
        //保存以当前字符结尾的最大连续字段和
        int dp[40000];
        //base case
        dp[0]=nums[0];
        for(int i=1;i<nums.size();i++)
        {
            dp[i]=max(nums[i],dp[i-1]+nums[i]);
        }
        int max=dp[0];
        for(int j=1;j<nums.size();j++)
        {
            //cout<<dp[j]<<endl;
            if(max<dp[j])
                max=dp[j];
        }
        return max;
    }
};

2.Leetcode416 Partition Equal Subset Sum

判断是否存在装法装在指定容量背包
Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
【KEY】:dp[i][j]表示对于前i个物体,存在某种方法,刚好装下j容量的背包
状态转移方程:

int[][] dp[N+1][W+1]
dp[0][..] = 0
dp[..][0] = 0

for i in [1..N]:
    for j in [1..W]:
        if (j - nums[i - 1] < 0) {
               // 背包容量不足,不能装入第 i 个物品
                dp[i][j] = dp[i - 1][j]; 
            } else {
                // 装入或不装入背包
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j-nums[i-1]];
            }
        
return dp[N][W]

源码:

class Solution {
public:
    bool canPartition(vector<int>& nums) 
    {
        
        int sum=0;
        for(int i=0;i<nums.size();i++)
        {
            sum+=nums[i];
        }
        if(sum%2!=0)
            return false;
        int ans=sum/2;
        cout<<ans;
        int size=nums.size();
        int dp[size+1][ans+1];
        memset(dp,0,sizeof(dp));
        //base case
       // dp[0][0]=1;
        for(int i=0;i<=size;i++)
        {
            dp[i][0]=1;
        }
        for(int i=1;i<=ans;i++)
        {
            dp[0][i]=0;
        }
        int save;
        //dp[i][j]表示对于前i个物体,存在某种方法,刚好装下j容量的背包
        for(int i=1;i<=nums.size();i++)
        {
            for(int j=1;j<=ans;j++)
            {
                save=nums[i-1];
                if(j-save<0)
                {
                    dp[i][j]=dp[i-1][j];
                    continue;
                }
                if(dp[i-1][j-save]==1)
                    dp[i][j]=1;
                else
                dp[i][j]=dp[i-1][j];
            }
        }
        
        if(dp[size][ans]==1)
            return true;
        else return false;
        
        
    }
};

3.【LeetCode518】Coin Change 2

给指定面额,返回有多少种可凑成该面额的方法?
You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.

You may assume that you have an infinite number of each kind of coin.

The answer is guaranteed to fit into a signed 32-bit integer.

Example 1:

Input: amount = 5, coins = [1,2,5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Example 3:

Input: amount = 10, coins = [10]
Output: 1

【注意】 coin change:给你 k 种面值的硬币,面值分别为 c1, c2 … ck,每种硬币的数量无限,再给一个总金额 amount,问你最少需要几枚硬币凑出这个金额
该题与coin change很类似,但区别在于coin change的状态:
dp 数组的定义:当目标金额为 i 时,至少需要 dp[i] 枚硬币凑出。
状态方程:

在这里插入图片描述
而对于有多少种凑法的状态为二维数组
首先看看刚才找到的「状态」,有两个,也就是说我们需要一个二维 dp 数组。
dp[i][j] 的定义如下:

若只使用前 i 个物品,当背包容量为 j 时,有 dp[i][j] 种方法可以装满背包。

换句话说,翻译回我们题目的意思就是:

若只使用 coins 中的前 i 个硬币的面值,若想凑出金额 j,有 dp[i][j] 种凑法。
(个人理解,若为dp[i]则在遍历每种金额的种类时,每个问题不是独立子问题,无法直接相加,所以需加上硬币面值这一状态)
【PS】该题base case很特别,是base case 为 dp[0][…] = 0, dp[…][0] = 1。因为如果不使用任何硬币面值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是唯一的一种凑法。

源码:

class Solution {
public:
    int change(int amount, vector<int>& coins)
    {
        int type=coins.size();
        //dp[i][j]表示前i种硬币可构成j面额的种数
        int dp[type+1][amount+1];
        memset(dp,0,sizeof(dp));
      for(int m=0;m<=amount;m++)
          dp[0][m]=1;
    for(int i=1;i<=coins.size();i++)
     {
        for(int j=1;j<=amount;j++)
        {
            if(coins[i-1]>j)
                dp[i][j]=dp[i-1][j];
            else 
            {
                dp[i][j]=dp[i-1][j]+dp[i][j-coins[i-1]];
            }
        }
    }
        return dp[type][amount];
    }
};
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值