ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

(六)动态规划【C++刷题】

2021-12-23 11:05:57  阅读:181  来源: 互联网

标签:nums int C++ heights height left 动态 dp 刷题


圆环回原点问题

1.问题描述

一个环,有n个点,编号从0增加,从原点0出发,每次只能走一步,每步可以顺时针到下一个点,也可以逆时针到上一个点,经过k步回到原点有多少种方法?

2.输入输出

  • Input:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], k=1/2
  • Output:0/2

3.算法分析

*【动态规划】回到0点可以从右面回来,也可以从左面回来,有多少种方法。

  • dp[k][j]表示从j点走k步到达原点0的方法树,转化为相邻的点经过k-1步回到原点的问题:

\[dp[k][j]=dp[k-1][j+1]+dp[k-1][j-1] \\ 【注:j-1和j+1会超出0至n-1范围】,因而转化为下式:\\ dp[k][j]=dp[k-1][(j-1+n)\%n]+dp[k-1][(j+1)\%n] \]

4.编程实现

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    int getStepNum(vector<int> &nums, int k) {
        int n = nums.size();  
        int dp[k+1][n];  //  dp[i][j]表示经过i步到达k点的方法数
        dp[0][0] = 1;  // 经过0步到达0点的方法数为1
        
        for (int i = 1; i < n; i++) {
            dp[0][i] = 0;
        }
        
        for (int i = 1; i <= k; i++) {
            for (int j = 0; j < n; j++) {
                dp[i][j] = dp[i-1][(j+1) % n] + dp[i-1][(j-1+n) % n];
            }
        }
        return dp[k][0];
    }
};

int main() {
    vector<int> nums = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int k = 2;
    Solution sol;
    
    cout << sol.getStepNum(nums, k);
}

接雨水

leetcode:https://leetcode-cn.com/problems/trapping-rain-water/
Nowcoder:https://www.nowcoder.com/practice/31c1aed01b394f0b8b7734de0324e00f?tpId=188

1.问题描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

2.输入输出

  • Input:height = [0,1,0,2,1,0,1,3,2,1,2,1]
  • Output:6

3.算法分析

【解法一:动态规划】对于下标i,下雨后水能到达的最大高度等于下标i两边的最大高度的最小值,下标i处能接到的雨水量等于下标i处的水能到达的最大高度减去height[i]。

思路:从左向右扫描得到每个height获得到左边最大高度,从右到左扫描得到每个height获得右边最大高度,最后遍历一遍每个下标位置即可得到能接的雨水总量。即两个dp数组。

  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(n)\)

【解法二:单调栈】维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组height中的元素递减。

(1)从左到右遍历数组,遍历到下标i时,如果栈内至少有两个元素,记栈顶元素为top,top的下面一个元素是left,则一定有height[left]≥height[top]。如果height[i]>height[top],则得到一个可以接雨水的区域,该区域的宽度是i-left-1,高度是min(height[left], height[i]-height[top]),根据宽度和高度计算得到该区域能接的雨水量。

(2)为了得到left,需要将top出栈。对top计算能接的雨水量之后,left变成新的top,重复上述操作,直到栈为空,或者栈顶下标对应的hegiht中的元素大于或等于height[i]。

(3)在对下标i处计算能接的雨水量之后,将i入栈,继续遍历后面的下标,计算能接的雨水量。遍历结束后得到能接的雨水量。

  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(n)\)

【解法三:双指针】动态规划中,将leftMax和rightMax最小值决定,从左往右和从右往左,用双指针和两个变量代替两个数组。

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

4.编程实现

// #include <bits/stdio.h>
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

class Solution {
public:
  int trap1(vector<int> &heights) {
      // 动态规划法
      int n = heights.size();
      if (n == 0) return 0;
      
      vector<int> leftMax(n, 0);
      leftMax[0] = heights[0];
      for (int i = 1; i < n; i++) {
          leftMax[i] = max(leftMax[i-1], heights[i]);
      }
      
      vector<int> rightMax(n, 0);
      rightMax[n-1] = heights[n-1];
      for (int i = n-2; i >= 0; i--) {
          rightMax[i] = max(rightMax[i+1], heights[i]);
      }
      
      int ans = 0;
      for (int i = 0; i < n; i++) {
          ans += min(leftMax[i], rightMax[i]) - heights[i];
      }
      
      return ans;
  }  
  int trap2(vector<int> &heights) {
      // 单调栈法
      int n = heights.size(), ans = 0;
      stack<int> stk;
      
      for (int i = 0; i < n; i++) {
          while (!stk.empty() && heights[i] > heights[stk.top()]) {
              int top = stk.top();
              stk.pop();
              if (stk.empty()) break;
              
              int left = stk.top();
              int currWidth = i - left - 1;
              int currHeight = min(heights[left], heights[i]) - heights[top];
              ans += currWidth * currHeight;
          }
          stk.push(i);
      }
      
      return ans;
  }
  int trap3(vector<int> &heights) {
      // 双指针——dp空间压缩法
      int ans = 0, left = 0, right = heights.size() - 1;
      int leftMax = 0, rightMax = 0;
      
      while (left < right) {
          leftMax = max(leftMax, heights[left]);
          rightMax = max(rightMax, heights[right]);
          
          if (heights[left] < heights[right]) {
              ans += leftMax - heights[left];
              ++left;
          } else {
              ans += rightMax - heights[right];
              --right;
          }
      }
      
      return ans;
  }
};
int main() {
    vector<int> heights;
    int val;
    Solution sol;
    
    getchar();
    while (cin >> val) {
        heights.push_back(val);
        if (cin.get() == ']') break;
    }
    cout << sol.trap3(heights) << endl;
    
    return 0;
}

最大子序和

Leetcode:https://leetcode-cn.com/problems/maximum-subarray/)

1.问题描述

给定一个整数数组nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

2.输入输出

​ Input:nums=[-2, -1, -3, 4, -1, 2, 1, -5, 4]

​ Output:6

3.算法分析

(1)划分子问题:每个数字都可以选择加入前一位的序列或者当前序列重新开始求和。

(2)定义子问题:定义一维数组dp[n],结果定义sum

(3)确定边界:dp[0] = nums[0]。

(4)状态转移方程确定:dp[i]=max(dp[i-1]+nums[i], nums[i]),sum为max(sum, dp[i])

4.编程实现

#include <iostream> 
#include <string> 
#include <vector> 
using namespace std; 


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

int main() { 
    int get;
    Solution sol;
    vector<int> nums; 
    
    getchar();
    while (cin >> get) { 
        nums.push_back(get); 
        if (cin.get() == ']') break; 
    } 
    
    cout << sol.maxSubArray(nums) << endl; 
    return 0; 

} 

标签:nums,int,C++,heights,height,left,动态,dp,刷题
来源: https://www.cnblogs.com/caoer/p/15722425.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有