題目描述
? ? ? ? "吃貨"和"饞嘴"兩人到披薩店點了一份鐵盤(圓形)披薩,并囑咐店員將披薩按放射狀切成大小相同的偶數個小塊。但是粗心的服務員將披薩切成了每塊大小都完全不同的奇數塊,且肉眼能分辨出大小。
? ? ? ?由于兩人都想吃到最多的披薩,他們商量了一個他們認為公平的分法:從"吃貨"開始,輪流取披薩。除了第一塊披薩可以任意選取外,其他都必須從缺口開始選。
他倆選披薩的思路不同。"饞嘴"每次都會選最大塊的披薩,而且"吃貨"知道"饞嘴"的想法。
問題:?已知披薩小塊的數量以及每塊的大小,求"吃貨"能分得的最大的披薩大小的總和。
解題思路
關鍵觀察:
- 圓形轉線性:吃貨第一步可以任意選擇,這會將圓形披薩變成線性數組
- 博弈策略:吃貨知道饞嘴總是選最大的,可以利用這個信息制定最優策略
- 動態規劃:后續變成經典的區間博弈問題
算法步驟:
- 枚舉吃貨第一塊的所有可能選擇
- 對于每種選擇,將剩余部分轉化為線性博弈問題
- 使用動態規劃求解最優策略
- 返回所有可能中的最大值
代碼實現/C++
#include <iostream>
#include <vector>
#include <algorithm>class PizzaGame {
private:// 解決線性數組的博弈問題// player: 0=吃貨, 1=饞嘴// 返回吃貨能獲得的最大收益int solveLinear(std::vector<int>& arr, int left, int right, int player,std::vector<std::vector<std::vector<int>>>& memo) {if (left > right) return 0;if (memo[left][right][player] != -1) {return memo[left][right][player];}int result;if (player == 0) { // 吃貨的回合// 吃貨選擇能讓自己最終收益最大的策略result = std::max(arr[left] + solveLinear(arr, left + 1, right, 1, memo),arr[right] + solveLinear(arr, left, right - 1, 1, memo));} else { // 饞嘴的回合// 饞嘴總是選最大的if (arr[left] >= arr[right]) {result = solveLinear(arr, left + 1, right, 0, memo);} else {result = solveLinear(arr, left, right - 1, 0, memo);}}return memo[left][right][player] = result;}public:int maxPizzaForChihuo(std::vector<int>& pizzaSizes) {int n = pizzaSizes.size();int maxResult = 0;// 枚舉第一塊披薩的選擇for (int start = 0; start < n; start++) {int currentResult = pizzaSizes[start];if (n > 1) {// 構建剩余的線性數組std::vector<int> remaining;for (int i = 1; i < n; i++) {remaining.push_back(pizzaSizes[(start + i) % n]);}// 初始化記憶化數組int remainSize = remaining.size();std::vector<std::vector<std::vector<int>>> memo(remainSize, std::vector<std::vector<int>>(remainSize, std::vector<int>(2, -1)));// 解決剩余的線性博弈問題(饞嘴先手)currentResult += solveLinear(remaining, 0, remainSize - 1, 1, memo);}maxResult = std::max(maxResult, currentResult);}return maxResult;}
};
?測試用例
int main() {PizzaGame game;// 測試用例1: [1, 3, 7, 5, 2]std::vector<int> pizza1 = {1, 3, 7, 5, 2};std::cout << "測試1: [1,3,7,5,2] -> " << game.maxPizzaForChihuo(pizza1) << std::endl;// 輸出: 11 (選擇7開始,然后得到7+2+1+1=11)// 測試用例2: [2, 1, 3, 4, 6, 5, 7] std::vector<int> pizza2 = {2, 1, 3, 4, 6, 5, 7};std::cout << "測試2: [2,1,3,4,6,5,7] -> " << game.maxPizzaForChihuo(pizza2) << std::endl;// 測試用例3: [10, 1, 2, 3, 4]std::vector<int> pizza3 = {10, 1, 2, 3, 4};std::cout << "測試3: [10,1,2,3,4] -> " << game.maxPizzaForChihuo(pizza3) << std::endl;// 輸出: 14 (選擇10開始,然后得到10+4=14)return 0;
}
?復雜度分析
時間復雜度:?O(n3)
- 外層枚舉起點:O(n)
- 內層動態規劃:O(n2)
- 總體:O(n3)
空間復雜度:?O(n2)
- 記憶化數組空間
解題要點
關鍵洞察:
- 圓形特殊性:第一步可以任意選擇,將圓形轉化為線性問題
- 對手策略:利用已知的對手貪心策略制定最優方案
- 狀態轉移:區間DP的經典應用
易錯點:
- 忘記考慮圓形數組的環形特性
- 沒有正確處理博弈雙方的不同策略
- 邊界條件處理不當