日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

运用贪心思想解决跳跃游戏

發(fā)布時(shí)間:2024/4/11 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 运用贪心思想解决跳跃游戏 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

運(yùn)用貪心思想解決跳躍游戲

文章目錄

    • 運(yùn)用貪心思想解決跳躍游戲
    • Jump Game I
      • 1.題目描述
      • 2.分析
      • 3.代碼
    • Jump Game II
      • 1.問題描述
      • 2.分析
      • 3.動規(guī)代碼【超時(shí)】
      • 4.貪心代碼

Jump Game I

1.題目描述

2.分析

  • 不知道讀者有沒有發(fā)現(xiàn),有關(guān)動態(tài)規(guī)劃的問題,大多是讓你求最值的,比如最長子序列,最小編輯距離,最長公共子串等等等。這就是規(guī)律,因?yàn)閯討B(tài)規(guī)劃本身就是運(yùn)籌學(xué)里的一種求最值的算法。
  • 那么貪心算法作為特殊的動態(tài)規(guī)劃也是一樣,也一定是讓你求個(gè)最值。這道題表面上不是求最值,但是可以改一改:
  • 請問通過題目中的跳躍規(guī)則,最多能跳多遠(yuǎn)?如果能夠越過最后一格,返回 true,否則返回 false。
  • 所以說,這道題肯定可以用動態(tài)規(guī)劃求解的。但是由于它比較簡單,下一道題再用動態(tài)規(guī)劃和貪心思路進(jìn)行對比,現(xiàn)在直接上貪心的思路:

3.代碼

bool canJump(vector<int>& nums) {int n = nums.size();int farthest = 0;for (int i = 0; i < n - 1; i++) {// 不斷計(jì)算能跳到的最遠(yuǎn)距離farthest = max(farthest, i + nums[i]);// 可能碰到了 0,卡住跳不動了if (farthest <= i) return false;}return farthest >= n - 1; }
  • 你別說,如果之前沒有做過類似的題目,還真不一定能夠想出來這個(gè)解法。每一步都計(jì)算一下從當(dāng)前位置最遠(yuǎn)能夠跳到哪里,然后和一個(gè)全局最優(yōu)的最遠(yuǎn)位置 farthest 做對比,通過每一步的最優(yōu)解,更新全局最優(yōu)解,這就是貪心。

很簡單是吧?記住這一題的思路,看第二題,你就發(fā)現(xiàn)事情沒有這么簡單。。。

Jump Game II

1.問題描述

2.分析

現(xiàn)在的問題是,保證你一定可以跳到最后一格,請問你最少要跳多少次,才能跳過去。

  • 我們先來說說動態(tài)規(guī)劃的思路,采用自頂向下的遞歸動態(tài)規(guī)劃,可以這樣定義一個(gè) dp 函數(shù):
// 定義:從索引 p 跳到最后一格,至少需要 dp(nums, p) 步 int dp(vector<int>& nums, int p);
  • 我們想求的結(jié)果就是 dp(nums, 0),base case 就是當(dāng) p 超過最后一格時(shí),不需要跳躍:
if (p >= nums.size() - 1) {return 0; }
  • 我們可以暴力窮舉所有可能的跳法,通過備忘錄 memo 消除重疊子問題,取其中的最小值最為最終答案:

3.動規(guī)代碼【超時(shí)】

vector<int> memo; // 主函數(shù) int jump(vector<int>& nums) {int n = nums.size();// 備忘錄都初始化為 n,相當(dāng)于 INT_MAX// 因?yàn)閺?0 調(diào)到 n - 1 最多 n - 1 步memo = vector<int>(n, n);return dp(nums, 0); }int dp(vector<int>& nums, int p) {int n = nums.size();// base caseif (p >= n - 1) {return 0;}// 子問題已經(jīng)計(jì)算過if (memo[p] != n) {return memo[p];}int steps = nums[p];// 你可以選擇跳 1 步,2 步...for (int i = 1; i <= steps; i++) {// 窮舉每一個(gè)選擇// 計(jì)算每一個(gè)子問題的結(jié)果int subProblem = dp(nums, p + i);// 取其中最小的作為最終結(jié)果memo[p] = min(memo[p], subProblem + 1);}return memo[p]; }
  • 該算法的時(shí)間復(fù)雜度是 遞歸深度 × 每次遞歸需要的時(shí)間復(fù)雜度,即 O(N^2),在 LeetCode上是無法通過所有用例的,會超時(shí)。

4.貪心代碼

  • 貪心算法比動態(tài)規(guī)劃多了一個(gè)性質(zhì):貪心選擇性質(zhì)。我知道大家都不喜歡看嚴(yán)謹(jǐn)?shù)菰锏臄?shù)學(xué)形式定義,那么我們就來直觀地看一看什么樣的問題滿足貪心選擇性質(zhì)。
  • 剛才的動態(tài)規(guī)劃思路,不是要窮舉所有子問題,然后取其中最小的作為結(jié)果嗎?核心的代碼框架是這樣:
int steps = nums[p];// 你可以選擇跳 1 步,2 步...for (int i = 1; i <= steps; i++) {// 計(jì)算每一個(gè)子問題的結(jié)果int subProblem = dp(nums, p + i);res = min(subProblem + 1, res);}
  • for 循環(huán)中會陷入遞歸計(jì)算子問題,這是動態(tài)規(guī)劃時(shí)間復(fù)雜度高的根本原因。
  • 但是,真的需要【遞歸地】計(jì)算出每一個(gè)子問題的結(jié)果,然后求最值嗎?直觀地想一想,似乎不需要遞歸,只需要判斷哪一個(gè)選擇最具有【潛力】即可:

  • 比如上圖這種情況,我們站在索引 0 的位置,可以向前跳 1,2 或 3 步,你說應(yīng)該選擇跳多少呢?
  • 顯然應(yīng)該跳 2 步調(diào)到索引 2,因?yàn)?nums[2] 的可跳躍區(qū)域涵蓋了索引區(qū)間 [3..6],比其他的都大。如果想求最少的跳躍次數(shù),那么往索引 2 跳必然是最優(yōu)的選擇。
  • 你看,這就是貪心選擇性質(zhì),我們不需要【遞歸地】計(jì)算出所有選擇的具體結(jié)果然后比較求最值,而只需要做出那個(gè)最有【潛力】,看起來最優(yōu)的選擇即可。
  • 繞過這個(gè)彎兒來,就可以寫代碼了
int jump(vector<int>& nums) {int n = nums.size();int end = 0, farthest = 0;int jumps = 0;for (int i = 0; i < n - 1; i++) {farthest = max(nums[i] + i, farthest);if (end == i) {jumps++;end = farthest;}}return jumps; }

結(jié)合剛才那個(gè)圖,就知道這段短小精悍的代碼在干什么了:

  • i 和 end 標(biāo)記了可以選擇的跳躍步數(shù),farthest 標(biāo)記了所有選擇 [i..end] 中能夠跳到的最遠(yuǎn)距離,jumps記錄了跳躍次數(shù)。
  • 本算法的 時(shí)間復(fù)雜度 O(N),空間復(fù)雜度 O(1), 可以說是非常高效,動態(tài)規(guī)劃都被吊起來打了。
  • 至此,兩道跳躍問題都使用貪心算法解決了。

總結(jié)

以上是生活随笔為你收集整理的运用贪心思想解决跳跃游戏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。