312. Burst Balloons

Description

Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and rightare adjacent indices of i. After the burst, the left and right then becomes adjacent.

Find the maximum coins you can collect by bursting the balloons wisely.

Note:
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 167

nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 3 * 1 * 5 + 3 * 5 * 8 + 1 * 3 * 8 + 1 * 8 * 1 = 167

Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.

Solution

Divide and Conquer, time O(n ^ 3), space O(n ^ 2)

腦洞大開的一道題关炼,正著不行反著想午衰。

Be Naive First

When I first get this problem, it is far from dynamic programming to me. I started with the most naive idea the backtracking.

We have n balloons to burst, which mean we have n steps in the game. In the i th step we have n-i balloons to burst, i = 0~n-1. Therefore we are looking at an algorithm of O(n!). Well, it is slow, probably works for n < 12 only.

Of course this is not the point to implement it. We need to identify the redundant works we did in it and try to optimize.

Well, we can find that for any balloons left the maxCoins does not depends on the balloons already bursted. This indicate that we can use memorization (top down) or dynamic programming (bottom up) for all the cases from small numbers of balloon until n balloons. How many cases are there? For k balloons there are C(n, k) cases and for each case it need to scan the k balloons to compare. The sum is quite big still. It is better than O(n!) but worse than O(2^n).

Better idea

We then think can we apply the divide and conquer technique? After all there seems to be many self similar sub problems from the previous analysis.

Well, the nature way to divide the problem is burst one balloon and separate the balloons into 2 sub sections one on the left and one one the right. However, in this problem the left and right become adjacent and have effects on the maxCoins in the future.

Then another interesting idea come up. Which is quite often seen in dp problem analysis. That is reverse thinking. Like I said the coins you get for a balloon does not depend on the balloons already burst. Therefore instead of divide the problem by the first balloon to burst, we divide the problem by the last balloon to burst.

Why is that? Because only the first and last balloons we are sure of their adjacent balloons before hand!

For the first we have nums[i-1]nums[i]nums[i+1] for the last we have nums[-1]nums[i]nums[n].

OK. Think about n balloons if i is the last one to burst, what now?

We can see that the balloons is again separated into 2 sections. But this time since the balloon i is the last balloon of all to burst, the left and right section now has well defined boundary and do not affect each other! Therefore we can do either recursive method with memoization or dp.

Final

Here comes the final solutions. Note that we put 2 balloons with 1 as boundaries and also burst all the zero balloons in the first round since they won’t give any coins.
The algorithm runs in O(n^3) which can be easily seen from the 3 loops in dp solution.

class Solution {
    public int maxCoins(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int n = 1;
        int[] positiveNums= new int[nums.length + 2];
        for (int x : nums) {
            if (x == 0) {   // remove zeros from nums because it doesn't earn coins
                continue;
            }
            positiveNums[n++] = x;
        }
        
        positiveNums[0] = 1;
        positiveNums[n++] = 1;
        
        int[][] maxCoins = new int[n][n];
        return maxCoinsRecur(positiveNums, 0, n - 1, maxCoins);
    }
    
    // left and right are non-exclusive boundries
    public int maxCoinsRecur(int[] nums, int left, int right, int[][] maxCoins) {
        if (left + 1 == right) {
            return 0;
        }
        
        if (maxCoins[left][right] > 0) {
            return maxCoins[left][right];
        }
        
        for (int i = left + 1; i < right; ++i) {
            int coins = nums[left] * nums[i] * nums[right] 
                + maxCoinsRecur(nums, left, i, maxCoins)
                + maxCoinsRecur(nums, i, right, maxCoins);
            maxCoins[left][right] = Math.max(maxCoins[left][right], coins);
        }
        
        return  maxCoins[left][right];
    } 
}

DP, time O(n ^ 3), space O(n ^ 2)

根據(jù)上面的思路都办,可以將其輕松轉(zhuǎn)換成DP稍算。需要注意的是DP計算的順序,由于可能依賴中間的計算結(jié)果恐锦,DP需要按照步長有小到大計算晋南,才能保證子問題已經(jīng)被處理過磷雇。

class Solution {
    public int maxCoins(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int n = 1;
        int[] positiveNums= new int[nums.length + 2];
        for (int x : nums) {
            if (x == 0) {   // remove zeros from nums because it doesn't earn coins
                continue;
            }
            positiveNums[n++] = x;
        }
        
        positiveNums[0] = 1;
        positiveNums[n++] = 1;
        
        int[][] maxCoins = new int[n][n];
        
        for (int step = 2; step < n; ++step) {
            for (int left = 0; left < n - step; ++left) {
                int right = left + step;
                for (int i = left + 1; i < right; ++i) {
                    int coins = positiveNums[i] * positiveNums[left] * positiveNums[right]
                        + maxCoins[left][i] + maxCoins[i][right];
                    maxCoins[left][right] = Math.max(maxCoins[left][right], coins);
                }
            }
        }
        
        return maxCoins[0][n - 1];
    } 
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市望艺,隨后出現(xiàn)的幾起案子苛秕,更是在濱河造成了極大的恐慌,老刑警劉巖找默,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艇劫,死亡現(xiàn)場離奇詭異,居然都是意外死亡惩激,警方通過查閱死者的電腦和手機店煞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咧欣,“玉大人浅缸,你說我怎么就攤上這事∑枪荆” “怎么了衩椒?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我毛萌,道長苟弛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任阁将,我火速辦了婚禮膏秫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘做盅。我一直安慰自己缤削,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布吹榴。 她就那樣靜靜地躺著亭敢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪图筹。 梳的紋絲不亂的頭發(fā)上帅刀,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音远剩,去河邊找鬼扣溺。 笑死,一個胖子當著我的面吹牛瓜晤,可吹牛的內(nèi)容都是我干的锥余。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼活鹰,長吁一口氣:“原來是場噩夢啊……” “哼哈恰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起志群,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤着绷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锌云,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荠医,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年桑涎,在試婚紗的時候發(fā)現(xiàn)自己被綠了彬向。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡攻冷,死狀恐怖娃胆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情等曼,我是刑警寧澤里烦,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布凿蒜,位于F島的核電站,受9級特大地震影響胁黑,放射性物質(zhì)發(fā)生泄漏废封。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一丧蘸、第九天 我趴在偏房一處隱蔽的房頂上張望漂洋。 院中可真熱鬧,春花似錦力喷、人聲如沸刽漂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爽冕。三九已至,卻和暖如春披蕉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乌奇。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工没讲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人礁苗。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓爬凑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親试伙。 傳聞我的和親對象是個殘疾皇子嘁信,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容