20200821动态规划(1.最长上升子序列 1.1 最大子序和 2.分割等和子集(子集背包问题) 3.注意初始化临时变量问题 零钱兑换II)

1.最长上升子序列
这道题和最长抖动序列(花匠)问题的区别在于 抖动序列每次递推 都为dp表保存了两个值 ,一个是上升表一个是下降表 这其中 无论当前值与前一个值的大小关系 都包含了取与不取的关系 也就是 这个表下来 包含了取任意多少的元素情况 因此最后结果直接cout即可

但是这道上升子序列问题 首先只有一个变量 就是当前位置 其次它只考虑了取当前值的问题 如果不取当前值(也就是数字未递增)就没有进行更新新递归表 所以它需要在指针走到每一位置 取一下最大值max 最终输出max而不是dp的终止值

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        if(n==0)return 0;
        vector<int>dp(n,1);
        int res=1;
        for(int i=1;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            res=max(res,dp[i]);
        }
        return res;
    }
};

试过了还是要按照这个来

1.1 最大子序和也要经过随时记录最大值
因为就算没有小于0 它也会比以前小 随时记录最值 跑不了

2.分割等和子集
这道题本来第一眼是打算用dfs 写的 两分钟就能写出来

    class Solution {
    public:
        bool flag=false;
        bool canPartition(vector<int>& nums) {
            dfs(0,0,0,nums);
            return flag;
        }
        void dfs(int a,int b,int cur,vector<int>&nums){
            if(cur==nums.size()){
                if(a==b){
                    flag=true;
                }
                return;
            }
            dfs(a+nums[cur],b,cur+1,nums);
            dfs(a,b+nums[cur],cur+1,nums);
        }
    };

但是发现超出时间限制了
只能作罢
方法是把它转为背包问题上来

如果可以等分 那么两个子集每一个都是sum/2
所以把这个数设为target 其次是 对于动态规划背包 这次dp不是求最多 而是能否 bool值dp
初始化时全部设为false 只把dp[0][0]为true 即可 然后就是 这个的递推公式是 用 || 而不是+1了

注意下标

    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 target=sum/2;

           bool dp[nums.size()+1][target+1];
           int bag=0;
           for(int i=0;i<=nums.size();i++)
           for(int j=0;j<=target;j++){
               dp[i][j]=false;
           }
           for(int i=0;i<=nums.size();i++){
                          dp[i][0]=true;

           }
          dp[0][0]=true;
           for(int i=1;i<=nums.size();i++){
               for(int j=1;j<=target;j++){
                   if(nums[i-1]>j){
                    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()][target];
        }
       
    };

其中赋值true循环可以删除

3.零钱兑换II
要注意:!!!临时变量自动初始化是随机值 所以定义变量要么在函数外开头全局变量 要么就临时变量一定要进行人为初始化

这道题和零钱兑换I的区别在于 要找最小金币数量 那么要对于每一个金额 都用所有的金币去试试 看谁最少 所以外循环是金额 内循环是金币
这道题是求所有可能的方法数 那么就从金币出发 对现有的每一个金币 去试试它所有能凑成的金额传递
所以外循环是金币 内循环是金额

class Solution {
public:
    int change(int amount, vector<int>& coins) {
       vector<int>dp(amount+1,0);
       dp[0]=1;
       for(auto coin:coins){
           for(int cnt=coin;cnt<=amount;cnt++){
               dp[cnt]+=dp[cnt-coin];

           }
       }
    return dp[amount];

    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值