zoukankan      html  css  js  c++  java
  • 动态规划简单

    动态规划四个步骤:1、确认最后一步,以及子问题 2、转移方程  3、初始值,边界条件 4、遍历方向(从左到右,从上到下)

    最大子序和

    给定一个数组,找到一个具有最大和的连续数组,返回其最大值。首先是一段连续的数组,这就涉及到了该位置上的数字选还是不选,要是最大的和,如果dp[i-1]>0的话,那么dp[i]=dp[i-1] + num[i],如果都dp[i-1] < 0 的2话,num[i]加上一个负数是小于它本身的,所以dp[i[ = num[i],而这道题我们需要知道前面一位的dp值,那么我们应该初始化第一位dp[0]=num[0],这里只需要循环到n-1,即可。所以申请空间也只需要n个,最后循环遍历dp数组获取最大值。时间复杂度O(n)。

    func maxSubArray(nums []int) int {
        max := nums[0]
        for i := 1; i < len(nums); i++ {
            if nums[i]+nums[i-1] > nums[i] {
                nums[i] += nums[i-1]
            }
            if nums[i] > max {
                max = nums[i]
            }
        }
        return max
    }
    
     1 class Solution {
     2 public:
     3     int maxSubArray(vector<int>& nums) {
     4         int ans = nums[0];
     5         for (int i = 1; i < nums.size(); i++) {
     6             if (nums[i-1] > 0) {
     7                 nums[i] = nums[i] + nums[i-1];
     8             }
     9             ans = max(ans, nums[i]);
    10         }
    11         return ans;
    12     }
    13 };
    View Code

    按摩师

    根据题意,给定一个数组。如果选择第i位数,那么他的前面后面的数都不能选,找到最大值的一个集合,返回最大值。这道题同样的方式,确定最终问题,也就是最后一个数,我们有两种情况,①如果我们选择了最后一位,所以就存在 dp[i][0]=d[i-1][1]+num[i],②如果我们不选那就是dp[i][1] = max(dp[i-1][0],dp[i-1][1]),对于子问题同样的存在选与不选两种情况。所以需要一个初始值,dp[0][0] = num[0], dp[0][1] = 0,(0表示选,1表示不选)。因为求最大值,遍历方向就是从左到右,顺序遍历。

     1 class Solution {
     2 public:
     3     int massage(vector<int>& nums) {
     4         int n = (int)nums.size();
     5         if (n == 0) {
     6             return 0;
     7         }
     8         int dp0 = nums[0], dp1 = 0;
     9         for (int i = 1; i < n; i++) {
    10             int tp0 = dp1 + nums[i];
    11             int tp1 = max(dp0, dp1);
    12             dp0 = tp0;
    13             dp1 = tp1;
    14         }
    15         return max(dp0, dp1);
    16     }
    17 };
    View Code
    func massage(nums []int) int {
        n := len(nums)
        dp := make([][]int, n)
        if n == 0 {
            return 0
        }
        for index := range dp {
            dp[index] = make([]int, 2)
        }
        dp[0][0] = nums[0]
        dp[0][1] = 0
        max := func(a, b int) int{
            if a > b {
                return a
            }else {
                return b
            }
        }
        for i :=1; i < n; i++ {
            dp[i][0] = dp[i-1][1] + nums[i]
            dp[i][1] = max(dp[i-1][0], dp[i-1][1])
        }
        return max(dp[n-1][0],dp[n-1][1])
    }
    

    除数博弈

    一种博弈题,在最优解下先手是否必赢,通过规律可以发现,当数为偶数时,爱丽丝必赢,当为奇数时,鲍勃必赢。因为只有2才能最后到达1,而需要到达4,当爱丽丝面临着偶数情况时,可以选择最优解快速让自己到达2,而面对奇数时,能被奇数求余为0的只能是奇数,而当奇数减去奇数时,变为了偶数,然后当鲍勃开始时,则面临着和爱丽丝一样的情况,偶数时可以选择最优情况快速到达2,所以当数为偶数是返回true,当为奇数时返回false。

    func divisorGame(N int) bool {
        return N%2 == 0
    }
    

    买卖股票的最佳时机

    根据题意,你只能买进和卖出一次,求出能获得的最大利润。对于最后一天,我们应该判断是买进还是卖出,因为是求出最大值,如果我们这时候卖出则需要与前一次卖出进行对比,得到最大值,即dp[i][0] = (dp[i-1][0],dp[i-1][1]+num[i]),如果我们买进则需要选择在这之前最小的即dp[i][1] = min(dp[i-1][1],num[i]),因为我们要算获利多少,所以买进的时候应该为负数,此时我们就需要买进时的最大值。dp[i][1] = min(dp[i-1][1],-num[i]。前一次同样面临着相同的子问题。再来是初始值问题,dp[0][0] = 0,dp[0][1] = -num[i],第一次卖出是0,第一次买入为-num[0]。最后返回答案dp[len-1][0].

    func maxProfit(prices []int) int {
        n := len(prices)
        if n < 2 {
            return 0
        }
        dp := make([][]int, n) 
        for index := range dp {
            dp[index] = make([]int, 2)
        }
        dp[0][0] = 0
        dp[0][1] = -prices[0]
        max := func(a, b int) int {
            if a < b {
                return b
            }
            return a
        }
        for i := 1; i < n; i++ {
            dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i])
            dp[i][1] = max(dp[i-1][1],-prices[i])
        }
        return dp[n-1][0]
    }
    
     1 class Solution {
     2 public:
     3     int maxProfit(vector<int>& prices) {
     4         int n = prices.size();
     5         if (n == 0) {
     6             return 0;
     7         }
     8         int dp0 = 0, dp1 = -prices[0];
     9         for (int i = 1; i < n; i++) {
    10             int tp0 = max(dp0, dp1+prices[i]);
    11             int tp1 = max(dp1, -prices[i]);
    12             dp0 = tp0;
    13             dp1 = tp1;
    14         }
    15         return dp0;
    16     }
    17 };
    View Code

    使用最小花费爬楼梯

    根据题意可以知道,我们每次爬楼梯可以爬一步,也可以爬两步,初始位置可以在索引0和1上,那么我们考虑到达最后的花费有两种情况dp[i-1]+cost[i-1], dp[i-2]+cost[i-2], 我们需要求最小值所以dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]),初始值就是dp[0] = 0,dp[1] = 1。注意这里是要到最顶层,即最小值为dp[n]。

    func minCostClimbingStairs(cost []int) int {
        n := len(cost)
        ans := make([]int, n+1)
        ans[0] = 0
        ans[1] = 0
        if n == 0 {
            return 0
        }
        if n == 1{
            return cost[0]
        }
        min := func(a, b int) int {
            if a > b {
                return b
            }
            return a
        }
        if n == 2 {
            return min(cost[0], cost[1])
        }
        for i :=2; i <= n; i ++ {
            ans[i] = min(ans[i-1]+cost[i-1], ans[i-2]+cost[i-2])
        }
        return ans[n]
    }
    
     1 class Solution {
     2 public:
     3     int minCostClimbingStairs(vector<int>& cost) {
     4         int n = cost.size();
     5         if (n <= 1) {
     6             return 0;
     7         }
     8         vector<int> dp(n + 1, 0);
     9         dp[0] = 0;
    10         dp[1] = 0;
    11         for(int i = 2; i <= n; i++) {
    12             dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
    13         }
    14         return dp[n];
    15     }
    16 };
    View Code
  • 相关阅读:
    使用ViewPager实现三个fragment切换
    handler
    Android 源码解析之AsyncTask
    android的生命周期
    安卓在SQLiteOpenHelper类进行版本升级和降级
    安卓ListView操作的两种方法
    表格布局TableLayout
    线性布局和相对布局
    遇到tomcat端口被占用问题解决方案
    easyUI笔记09.03
  • 原文地址:https://www.cnblogs.com/cxylsy/p/13797860.html
Copyright © 2011-2022 走看看