在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

經典動態規劃:戳氣球問題

算法與數據結構 ? 來源:算法與數據結構 ? 2020-06-03 17:29 ? 次閱讀

今天我們要聊的這道題「Burst Balloon」和之前我們寫過的那篇 經典動態規劃:高樓扔雞蛋問題 分析過的高樓扔雞蛋問題類似,知名度很高,但難度確實也很大。因此 labuladong 公眾號就給這道題賜個座,來看一看這道題目到底有多難。

它是 LeetCode 第 312 題,題目如下:

title

首先必須要說明,這個題目的狀態轉移方程真的比較巧妙,所以說如果你看了題目之后完全沒有思路恰恰是正常的。雖然最優答案不容易想出來,但基本的思路分析是我們應該力求做到的。所以本文會先分析一下常規思路,然后再引入動態規劃解法。

一、回溯思路

先來順一下解決這種問題的套路:

我們前文多次強調過,很顯然只要涉及求最值,沒有任何奇技淫巧,一定是窮舉所有可能的結果,然后對比得出最值。

所以說,只要遇到求最值的算法問題,首先要思考的就是:如何窮舉出所有可能的結果?

窮舉主要有兩種算法,就是回溯算法和動態規劃,前者就是暴力窮舉,而后者是根據狀態轉移方程推導「狀態」。

如何將我們的扎氣球問題轉化成回溯算法呢?這個應該不難想到的,我們其實就是想窮舉戳氣球的順序,不同的戳氣球順序可能得到不同的分數,我們需要把所有可能的分數中最高的那個找出來,對吧。

那么,這不就是一個「全排列」問題嘛,我們前文 回溯算法框架套路詳解 中有全排列算法的詳解和代碼,其實只要稍微改一下邏輯即可,偽碼思路如下:

intres=Integer.MIN_VALUE; /*輸入一組氣球,返回戳破它們獲得的最大分數*/ intmaxCoins(int[]nums){ backtrack(nums,0); returnres; } /*回溯算法的偽碼解法*/ voidbacktrack(int[]nums,intsocre){ if(nums為空){ res=max(res,score); return; } for(inti=0;i

回溯算法就是這么簡單粗暴,但是相應的,算法的效率非常低。這個解法等同于全排列,所以時間復雜度是階乘級別,非常高,題目說了nums的大小n最多為 500,所以回溯算法肯定是不能通過所有測試用例的。

二、動態規劃思路

這個動態規劃問題和我們之前的動態規劃系列文章相比有什么特別之處?為什么它比較難呢?

原因在于,這個問題中我們每戳破一個氣球nums[i],得到的分數和該氣球相鄰的氣球nums[i-1]和nums[i+1]是有相關性的。

我們前文動態規劃套路框架詳解 說過運用動態規劃算法的一個重要條件:子問題必須獨立。所以對于這個戳氣球問題,如果想用動態規劃,必須巧妙地定義dp數組的含義,避免子問題產生相關性,才能推出合理的狀態轉移方程。

如何定義dp數組呢,這里需要對問題進行一個簡單地轉化。題目說可以認為nums[-1] = nums[n] = 1,那么我們先直接把這兩個邊界加進去,形成一個新的數組points:

intmaxCoins(int[]nums){ intn=nums.length; //兩端加入兩個虛擬氣球 int[]points=newint[n+2]; points[0]=points[n+1]=1; for(inti=1;i<=?n;?i++)?{ ????????points[i]?=?nums[i?-?1]; ????} ????//?... }

現在氣球的索引變成了從1到n,points[0]和points[n+1]可以認為是兩個「虛擬氣球」。

那么我們可以改變問題:在一排氣球points中,請你戳破氣球0和氣球n+1之間的所有氣球(不包括0和n+1),使得最終只剩下氣球0和氣球n+1兩個氣球,最多能夠得到多少分?

現在可以定義dp數組的含義:

dp[i][j] = x表示,戳破氣球i和氣球j之間(開區間,不包括i和j)的所有氣球,可以獲得的最高分數為x。

那么根據這個定義,題目要求的結果就是dp[0][n+1]的值,而 base case 就是dp[i][j] = 0,其中0 <= i <= n+1, j <= i+1,因為這種情況下,開區間(i, j)中間根本沒有氣球可以戳。

//basecase已經都被初始化為0 int[][]dp=newint[n+2][n+2];

現在我們要根據這個dp數組來推導狀態轉移方程了,根據我們前文的套路,所謂的推導「狀態轉移方程」,實際上就是在思考怎么「做選擇」,也就是這道題目最有技巧的部分:

不就是想求戳破氣球i和氣球j之間的最高分數嗎,如果「正向思考」,就只能寫出前文的回溯算法;我們需要「反向思考」,想一想氣球i和氣球j之間最后一個被戳破的氣球可能是哪一個?

其實氣球i和氣球j之間的所有氣球都可能是最后被戳破的那一個,不防假設為k。回顧動態規劃的套路,這里其實已經找到了「狀態」和「選擇」:i和j就是兩個「狀態」,最后戳破的那個氣球k就是「選擇」。

根據剛才對dp數組的定義,如果最后一個戳破氣球k,dp[i][j]的值應該為:

dp[i][j]=dp[i][k]+dp[k][j] +points[i]*points[k]*points[j]

你不是要最后戳破氣球k嗎?那得先把開區間(i, k)的氣球都戳破,再把開區間(k, j)的氣球都戳破;最后剩下的氣球k,相鄰的就是氣球i和氣球j,這時候戳破k的話得到的分數就是points[i]*points[k]*points[j]。

那么戳破開區間(i, k)和開區間(k, j)的氣球最多能得到的分數是多少呢?嘿嘿,就是dp[i][k]和dp[k][j],這恰好就是我們對dp數組的定義嘛!

結合這個圖,就能體會出dp數組定義的巧妙了。由于是開區間,dp[i][k]和dp[k][j]不會影響氣球k;而戳破氣球k時,旁邊相鄰的就是氣球i和氣球j了,最后還會剩下氣球i和氣球j,這也恰好滿足了dp數組開區間的定義。

那么,對于一組給定的i和j,我們只要窮舉i < k < j的所有氣球k,選擇得分最高的作為dp[i][j]的值即可,這也就是狀態轉移方程:

//最后戳破的氣球是哪個? for(intk=i+1;k

寫出狀態轉移方程就完成這道題的一大半了,但是還有問題:對于k的窮舉僅僅是在做「選擇」,但是應該如何窮舉「狀態」i和j呢?

for(inti=...;;) for(intj=...;;) for(intk=i+1;k

三、寫出代碼

關于「狀態」的窮舉,最重要的一點就是:狀態轉移所依賴的狀態必須被提前計算出來。

拿這道題舉例,dp[i][j]所依賴的狀態是dp[i][k]和dp[k][j],那么我們必須保證:在計算dp[i][j]時,dp[i][k]和dp[k][j]已經被計算出來了(其中i < k < j)。

那么應該如何安排i和j的遍歷順序,來提供上述的保證呢?我們前文 動態規劃答疑篇 寫過處理這種問題的一個雞賊技巧:根據 base case 和最終狀態進行推導。

PS:最終狀態就是指題目要求的結果,對于這道題目也就是dp[0][n+1]。

我們先把 base case 和最終的狀態在 DP table 上畫出來:

對于任一dp[i][j],我們希望所有dp[i][k]和dp[k][j]已經被計算,畫在圖上就是這種情況:

那么,為了達到這個要求,可以有兩種遍歷方法,要么斜著遍歷,要么從下到上從左到右遍歷:

斜著遍歷有一點難寫,所以一般我們就從下往上遍歷,下面看完整代碼:

intmaxCoins(int[]nums){ intn=nums.length; //添加兩側的虛擬氣球 int[]points=newint[n+2]; points[0]=points[n+1]=1; for(inti=1;i<=?n;?i++)?{ ????????points[i]?=?nums[i?-?1]; ????} ????//?base?case?已經都被初始化為?0 ????int[][]?dp?=?new?int[n?+?2][n?+?2]; ????//?開始狀態轉移 ????//?i?應該從下往上 ????for?(int?i?=?n;?i?>=0;i--){ //j應該從左往右 for(intj=i+1;j

至此,這道題目就完全解決了,十分巧妙,但也不是那么難,對吧?

關鍵在于dp數組的定義,需要避免子問題互相影響,所以我們反向思考,將dp[i][j]的定義設為開區間,考慮最后戳破的氣球是哪一個,以此構建了狀態轉移方程。

對于如何窮舉「狀態」,我們使用了小技巧,通過 base case 和最終狀態推導出i,j的遍歷方向,保證正確的狀態轉移。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 算法
    +關注

    關注

    23

    文章

    4622

    瀏覽量

    93053
  • 代碼
    +關注

    關注

    30

    文章

    4802

    瀏覽量

    68738

原文標題:經典動態規劃:戳氣球問題

文章出處:【微信號:TheAlgorithm,微信公眾號:算法與數據結構】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Java中時間的使用

    Java中時間的使用
    的頭像 發表于 11-06 16:04 ?233次閱讀
    Java中時間<b class='flag-5'>戳</b>的使用

    多臺倉儲AGV協作全局路徑規劃算法的研究

    多AGV動態路徑規劃需解決沖突避免,核心在整體協調最優。規劃時考慮道路設計、擁堵、最短路徑和交通管制,用A*算法避免重復路徑和轉彎,同時需交通管制防相撞。創新響應需求是關鍵,良好路徑規劃
    的頭像 發表于 10-28 17:38 ?314次閱讀
    多臺倉儲AGV協作全局路徑<b class='flag-5'>規劃</b>算法的研究

    同星多設備間的時間同步機制TSync功能與使用

    隨著汽車網絡測試的通道數量不斷增加,時常需要多個同星設備同時連接在同一臺電腦的同一個TSMaster應用程序,并進行多設備同時執行CAN報文收發和記錄等功能,必然有多設備之間的時間同步以及設備
    的頭像 發表于 10-12 08:05 ?2860次閱讀
    同星多設備間的時間<b class='flag-5'>戳</b>同步機制TSync功能與使用

    物聯網中的普通傳感器如何通過DTU/RTU透傳帶有時間和IMEI的數據

    一 概述 時間是指格林威治時間1970年01月01日00時00分00秒(北京時間1970年01月01日08時00分00秒)起至現在的總秒數。時間是使用數字簽名技術產生的數據,簽名的對象包括了原始
    的頭像 發表于 09-25 16:35 ?1307次閱讀
    物聯網中的普通傳感器如何通過DTU/RTU透傳帶有時間<b class='flag-5'>戳</b>和IMEI的數據

    esp32獲取時間的相關函數是哪個?

    esp32 獲取時間的相關函數是哪個,我用了sntp_get_current_timestamp這個函數,編譯的時候說找不到,有沒有相關的例程參考?
    發表于 06-11 07:26

    如何在TLE9893系列中實現RTC時間?

    我正試圖在 TLE9893 系列中實現 RTC 時間。 能否提供一個基本 RTC 示例的想法或示例源項目?
    發表于 05-27 06:20

    什么是經典藍牙模塊?

    ? 什么是經典藍牙模塊? 前面我們已經就藍牙模塊的概念做了了解,隨著時間的推移,產品越來越智能,需要的藍牙模塊也就越來越廣泛,本篇文章我們就一起了解下什么是經典藍牙模塊。 經典藍牙模塊(BT)泛指
    的頭像 發表于 05-24 14:52 ?544次閱讀
    什么是<b class='flag-5'>經典</b>藍牙模塊?

    藍牙模塊選經典藍牙還是低功耗藍牙?

    ? 經典藍牙(classic Bluetooth)和低功耗藍牙(Bluetooth low energy,簡稱BLE或者LE),兩者有什么區別?為什么他們都叫“藍牙”?Bluetooth low
    的頭像 發表于 05-23 17:57 ?1321次閱讀
    藍牙模塊選<b class='flag-5'>經典</b>藍牙還是低功耗藍牙?

    STM32L051同時開啟RTC時間和秒喚醒后,使用GPIO引腳無法觸發時間中斷如何解決?

    STM32L051同時開啟RTC時間和秒喚醒后,使用GPIO引腳無法觸發時間中斷。但使用一個3.3V的電源接到PC13能夠觸發RTC時間。 我將PB8和PC13相連,PB8特定情況下會輸出脈沖
    發表于 04-01 06:37

    stm8s208 can時間觸發模式沒有發出時間的原因?

    需要發送一個標準數據幀,DLC為8,使用下面函數開啟時間觸發通訊模式,串口打印確認TTCM和TGT位置1,而且發送時間寄存器有數據變化,但是沒有發出時間。 void
    發表于 03-19 07:08

    關于在can控制器上生成時間時遇到的疑問求解

    嗨, 我在 can 控制器上生成時間時遇到了問題。 1) 配置一個 1us 的 gtm 作為時間來源。 void initGtmTomPwm(void
    發表于 03-04 06:08

    不同的CAN通道同時收到CAN數據時,怎樣獲取時間呢?

    不同的CAN通道同時收到CAN數據時,怎樣獲取時間呢?按理說獲取的時間應該是同一時刻。
    發表于 02-18 08:03

    verilog的135個經典實例

    verilog的135個經典實例
    發表于 02-02 10:17 ?14次下載

    AURIX 3XX GTM-TIM捕獲EDSADC SRM發生時的時間一直調試不正常是怎么回事?

    各位大佬,目前在用英飛凌的TC36X系列的EDSADC模塊進行旋變軟解碼功能(參考AP32270),為了控制精度,其中提到了要進行時間補償,推薦采用的是使用GTM-TIM模塊捕獲DSADC 結果
    發表于 01-26 08:12

    在Tc397中如何用Tim捕捉時間

    請問各大神在Tc397中如何用Tim捕捉時間
    發表于 01-22 08:09
    主站蜘蛛池模板: 毛片视频网址| 久久黄视频| 亚洲电影一区二区三区| 午夜精品在线| 久久久精品免费国产四虎| 涩五月婷婷| 一级片 在线播放| 免费日本视频| 97dyy影院理论片| 插综合网| 国产一级一级片| 一级特色黄大片| 大象焦伊人久久综合网色视| 久久99精品久久久久久野外 | 又黄又粗暴的120秒免费gif视频| 在线免费看一级片| 日本久久黄色| 天堂网www中文天堂在线| 91黄色视屏| 在线最新版www资源网| 手机看片91| 777影院| 91精品啪国产在线观看免费牛牛| ccav在线永久免费看| 最新国产你懂的在线网址| 国产亚洲一区二区三区啪| 亚洲视频二| 男人午夜| 色噜噜狠狠色综合欧洲| 色久月| 国产偷窥女洗浴在线观看亚洲| 国产精品国产三级国产在线观看| 国产免费一级高清淫曰本片| 丁香六月色婷婷| 手机午夜看片| 97se狠狠狠狠狼亚洲综合网| 亚洲精品久久久久影| 久色99| 操的好爽视频| 午夜在线观看免费| 免费观看老外特级毛片|