初級演算法-動態規劃

方丈的寺院發表於2018-07-15

摘要

本篇主要介紹動態規劃解題思路

概括

動態規劃主要是解一些遞迴問題,也就是將遞迴寫成非遞迴方式,因為編輯器無法正確對待遞迴,遞迴方法會導致很多計算結果被重複計算,比如菲波那切數列。

所以動態規劃的解題思路也就是
1. 列出遞迴表示式
2. 將遞迴方法寫成非遞迴方式

比如菲波那切數列
F(n) = F(n-1) + F(n-2)
使用兩個中間變數儲存之前的計算結果,就改寫成了非遞迴方式實現,也就是動態規劃。

常見的題

leetcode 動態規劃題
https://leetcode-cn.com/explore/interview/card/top-interview-questions-easy/23/dynamic-programming/

一. 爬樓梯

假設你正在爬樓梯。需要 n 步你才能到達樓頂。

每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?

按照解題方法,寫列出遞迴方程
設定dp[i] 表示走到某個臺階的方法,那麼就有遞迴方程

dp[1] = 1;dp[2] = 2(一次走一步/一次走二步)
dp[3] = dp[1] + dp[2] (從dp[1]中方法中,走2步上來,或者從dp[2]種方法中走1步上來)
dp[n] = dp[n-2] + dp[n-1]

寫成非遞迴方法

public static int climbStairs(int n) {
        if(n < 1) {
            return 0;
        }

        if (n == 1) {
            return 1;
        }

        if (n == 2) {
            return 2;
        }
        int dp[] = new int [] {1,2};
        int result = dp[0] + dp[1];
        for (int i = 3; i <= n; i++) {
            result = dp[0] + dp[1];
            dp[0] = dp[1];
            dp[1] = result;
        }
        return result;
    }

二. 最大子序列

給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

  1. 列出遞迴方程
    以 nums[-2,1,-3,4,-1,2,1,-5,4] 為例, dp[i]表示遍歷到第i個數時,當前最大連續子陣列和
    dp[1] = -2, dp[2] = Math.max(dp[1] + nums[2],nums[2]),因為要求是連續子陣列,所以nums[i]必須接上,
    然後再比較歷史最大的的連續子陣列和這次的最大值

程式碼如下

 public static int maxSubArray(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }

        if (nums.length == 1) {
            return nums[0];
        }
        int result =  Math.max(nums[0]+nums[1],nums[1]);
        int dp = result;
        int max = Math.max(result, nums[0]);
        for (int i = 2; i< nums.length; i++) {
            result = Math.max(dp+nums[i],nums[i]);
            dp = result;
            // 如果比歷史的大,就替換
            if (result > max) {
                max = result;
            }
        }
        return max;
    }

三. 打家劫舍

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。
給定一個代表每個房屋存放金額的非負整數陣列,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額

如 [2,7,9,3,1]
設定dp[i] 是偷i個房屋,所能偷竊到的最高金額,dp[1] = 2, dp[2] = 7, dp[3] = 2+9,Math.max(dp[1] + nums[3], dp[2])
遞迴方程

dp[i] = max(dp[i-2] + nums[i], dp[i-1])

非遞迴方法實現

 public static int rob(int[] nums) {
        if (nums.length ==0) {
            return 0;
        }
        if (nums.length == 1) {
            return nums[0];
        }

        if (nums.length  ==2) {
            return Math.max(nums[0],nums[1]);
        }

        int dp[] = new int[] {nums[0], Math.max(nums[0], nums[1])};
        int result=0;
        for (int i = 2; i < nums.length; i++) {
            result = Math.max(dp[0] + nums[i], dp[1]);
            dp[0] = dp[1];
            dp[1] = result;
        }
        return result;
    }

這裡寫圖片描述

相關文章