溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++如何實現打氣球游戲

發布時間:2022-03-28 14:09:06 來源:億速云 閱讀:218 作者:iii 欄目:大數據

這篇文章主要講解了“C++如何實現打氣球游戲”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C++如何實現打氣球游戲”吧!

打氣球游戲

Example:

Input:

[3,1,5,8]

Output:

167

Explanation:

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 @peisi for adding this problem and creating all test cases.

這道題提出了一種打氣球的游戲,每個氣球都對應著一個數字,每次打爆一個氣球,得到的金幣數是被打爆的氣球的數字和其兩邊的氣球上的數字相乘,如果旁邊沒有氣球了,則按1算,以此類推,求能得到的最多金幣數。參見題目中給的例子,題意并不難理解。那么大家拿到題后,總是會習慣的先去想一下暴力破解法吧,這道題的暴力搜索將相當的復雜,因為每打爆一個氣球,斷開的地方又重新挨上,所有剩下的氣球又要重新遍歷,這使得分治法不能 work,整個的時間復雜度會相當的高,不要指望可以通過 OJ。而對于像這種求極值問題,一般都要考慮用動態規劃 Dynamic Programming 來做,維護一個二維動態數組 dp,其中 dp[i][j] 表示打爆區間 [i,j] 中的所有氣球能得到的最多金幣。題目中說明了邊界情況,當氣球周圍沒有氣球的時候,旁邊的數字按1算,這樣可以在原數組兩邊各填充一個1,方便于計算。這道題的最難點就是找狀態轉移方程,還是從定義式來看,假如區間只有一個數,比如 dp[i][i],那么計算起來就很簡單,直接乘以周圍兩個數字即可更新。如果區間里有兩個數字,就要算兩次了,先打破第一個再打破了第二個,或者先打破第二個再打破第一個,比較兩種情況,其中較大值就是該區間的 dp 值。假如區間有三個數呢,比如 dp[1][3],怎么更新呢?如果先打破第一個,剩下兩個怎么辦呢,難道還要分別再遍歷算一下嗎?這樣跟暴力搜索的方法有啥區別呢,還要 dp 數組有啥意思。所謂的狀態轉移,就是假設已知了其他狀態,來推導現在的狀態,現在是想知道 dp[1][3] 的值,那么如果先打破了氣球1,剩下了氣球2和3,若之前已經計算了 dp[2][3] 的話,就可以使用其來更新 dp[1][3] 了,就是打破氣球1的得分加上 dp[2][3]。那假如先打破氣球2呢,只要之前計算了 dp[1][1] 和 dp[3][3],那么三者加起來就可以更新 dp[1][3]。同理,先打破氣球3,就用其得分加上 dp[1][2] 來更新 dp[1][3]。說到這里,是不是感覺豁然開朗了 ^.^

那么對于有很多數的區間 [i, j],如何來更新呢?現在是想知道 dp[i][j] 的值,這個區間可能比較大,但是如果知道了所有的小區間的 dp 值,然后聚沙成塔,逐步的就能推出大區間的 dp 值了。還是要遍歷這個區間內的每個氣球,就用k來遍歷吧,k在區間 [i, j] 中,假如第k個氣球最后被打爆,那么此時區間 [i, j] 被分成了三部分,[i, k-1],[k],和 [k+1, j],只要之前更新過了 [i, k-1] 和 [k+1, j] 這兩個子區間的 dp 值,可以直接用 dp[i][k-1] 和 dp[k+1][j],那么最后被打爆的第k個氣球的得分該怎么算呢,你可能會下意識的說,就乘以周圍兩個氣球被 nums[k-1] * nums[k] * nums[k+1],但其實這樣是錯誤的,為啥呢?dp[i][k-1] 的意義是什么呢,是打爆區間 [i, k-1] 內所有的氣球后的最大得分,此時第 k-1 個氣球已經不能用了,同理,第 k+1 個氣球也不能用了,相當于區間 [i, j] 中除了第k個氣球,其他的已經爆了,那么周圍的氣球只能是第 i-1 個,和第 j+1 個了,所以得分應為 nums[i-1] * nums[k] * nums[j+1],分析到這里,狀態轉移方程應該已經躍然紙上了吧,如下所示:

dp[i][j] = max(dp[i][j], nums[i - 1] * nums[k] * nums[j + 1] + dp[i][k - 1] + dp[k + 1][j])                 ( i ≤  j )

有了狀態轉移方程了,就可以寫代碼,下面就遇到本題的第二大難點了,區間的遍歷順序。一般來說,遍歷所有子區間的順序都是i從0到n,然后j從i到n,然后得到的 [i, j] 就是子區間。但是這道題用這種遍歷順序就不對,在前面的分析中已經說了,這里需要先更新完所有的小區間,然后才能去更新大區間,而用這種一般的遍歷子區間的順序,會在更新完所有小區間之前就更新了大區間,從而不一定能算出正確的dp值,比如拿題目中的那個例子 [3, 1, 5, 8] 來說,一般的遍歷順序是:

[3] -> [3, 1] -> [3, 1, 5] -> [3, 1, 5, 8] -> [1] -> [1, 5] -> [1, 5, 8] -> [5] -> [5, 8] -> [8] 

顯然不是我們需要的遍歷順序,正確的順序應該是先遍歷完所有長度為1的區間,再是長度為2的區間,再依次累加長度,直到最后才遍歷整個區間:

[3] -> [1] -> [5] -> [8] -> [3, 1] -> [1, 5] -> [5, 8] -> [3, 1, 5] -> [1, 5, 8] -> [3, 1, 5, 8]

這里其實只是更新了 dp 數組的右上三角區域,最終要返回的值存在 dp[1][n] 中,其中n是兩端添加1之前數組 nums 的個數。參見代碼如下:

解法一:

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
        for (int len = 1; len <= n; ++len) {
            for (int i = 1; i <= n - len + 1; ++i) {
                int j = i + len - 1;
                for (int k = i; k <= j; ++k) {
                    dp[i][j] = max(dp[i][j], nums[i - 1] * nums[k] * nums[j + 1] + dp[i][k - 1] + dp[k + 1][j]);
                }
            }
        }
        return dp[1][n];
    }
};

對于題目中的例子[3, 1, 5, 8],得到的dp數組如下:

0    0    0    0       0     0
0    3    30  159  167  0
0    0    15  135  159  0
0    0    0    40     48   0
0    0    0    0       40   0
0    0    0    0       0     0

這題還有遞歸解法,思路都一樣,就是寫法略有不同,參見代碼如下:

解法二:

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
        return burst(nums, dp, 1 , n);
    }
    int burst(vector<int>& nums, vector<vector<int>>& dp, int i, int j) {
        if (i > j) return 0;
        if (dp[i][j] > 0) return dp[i][j];
        int res = 0;
        for (int k = i; k <= j; ++k) {
            res = max(res, nums[i - 1] * nums[k] * nums[j + 1] + burst(nums, dp, i, k - 1) + burst(nums, dp, k + 1, j));
        }
        dp[i][j] = res;
        return res;
    }
};

感謝各位的閱讀,以上就是“C++如何實現打氣球游戲”的內容了,經過本文的學習后,相信大家對C++如何實現打氣球游戲這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女