🔗 🟡 2786. Visit Array Positions to Maximize Score 1733

tags: Biweekly Contest 109 動態規劃(DP)

題意

給定一個下標從 00 開始的整數陣列 numsnums 和一個正整數 xx

你一開始在陣列的 00 號位置,可以根據以下規則到其他位置:

  • 如果你現在在 ii 號位置,可以移動到任何滿足 i<ji < j 的位置 jj
  • 對於你到達的每個位置 ii,你可以獲得分數 nums[i]nums[i]
  • 如果你從位置 ii 移動到位置 jj ,但 nums[i]nums[i]nums[j]nums[j] 的奇偶性不同,那麼你將失去分數 xx

你可以移動到任何 jj 號位置,只要 i<ji < j

返回能夠獲得的 最大 總分。

請注意,一開始你有 nums[0]nums[0] 分。

約束條件

  • 2nums.length1052 \leq nums.length \leq 10^5
  • 1nums[i],x1061 \leq nums[i], x \leq 10^6

思路:動態規劃(DP)

寫法一

dp[i][p]dp[i][p] 表示考慮到第 ii 個位置,最後一個位置的奇偶性為 pp (0 表示偶數,1 表示奇數) 時,能獲得的最大分數。

當從位置 i1i-1 到達位置 ii 時,我們有兩個選擇:

  1. 如果 nums[i1]nums[i-1]nums[i]nums[i] 的奇偶性相同 (pp 相同),由於 nums[i]nums[i] 為正整數,選擇走到位置 ii 會使分數增加,因此 dp[i][p]dp[i][p] 可以從 dp[i1][p]+nums[i]dp[i-1][p] + nums[i] 獲得。
  2. 如果奇偶性不同,則 dp[i][p]dp[i][p] 可以從 dp[i1][1p]+nums[i]xdp[i-1][1-p] + nums[i] - x 獲得,其中 1p1-p 表示與 pp 相反的奇偶性。

故可以得到狀態轉移方程:

  • dp[i][p]=max(dp[i1][p]+nums[i],dp[i1][1p]+nums[i]x)dp[i][p] = \max(dp[i-1][p] + nums[i], dp[i-1][1-p] + nums[i] - x)
  • dp[i][1p]=dp[i1][1p]dp[i][1-p] = dp[i-1][1-p] ,表示當前位置的另一種奇偶性的最大得分不變。

具體做法如下:

  1. 初始化 dpdp 陣列,dp[i][p]dp[i][p] 表示考慮到第 ii 個位置,最後一個位置的奇偶性為 pp 時,能獲得的最大分數。
    • 由於從位置 00 開始,所以 dp[0][nums[0]mod2]=nums[0]dp[0][nums[0] \bmod 2] = nums[0]
    • dpdp 陣列的其他值初始化為負無窮,表示未達到該位置。
    • 初始化 ans=nums[0]\text{ans} = nums[0]
  2. 遍歷從 11n1n-1 的位置 ii。對於每個 ii,根據 nums[i]nums[i] 的奇偶性 p=nums[i]mod2p = nums[i] \bmod 2 更新 dp[i][p]dp[i][p]dp[i][1p]dp[i][1-p]
  3. 每一步都更新最大分數 ansansmax(ans,dp[i][p])\max(\text{ans}, dp[i][p])
  4. 最後返回 ansans 即可。

複雜度分析

  • 時間複雜度:O(n)\mathcal{O}(n),其中 nn 是陣列 numsnums 的長度,只需要遍歷一次陣列。
  • 空間複雜度:O(n)\mathcal{O}(n),使用了長度為 n×2n \times 2 的二維陣列 dpdp
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def maxScore(self, nums: List[int], x: int) -> int:
n = len(nums)
ans = nums[0]
dp = [[float('-inf'), float('-inf')] for _ in range(n)]
dp[0][nums[0] % 2] = nums[0]
for i in range(1, n):
p = nums[i] % 2 # parity, 0: even, 1: odd
dp[i][p] = max(dp[i - 1][p] + nums[i], dp[i - 1][1 - p] + nums[i] - x)
dp[i][1 - p] = dp[i - 1][1 - p]
ans = max(ans, dp[i][p])
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const int INF = 0x3f3f3f3f;
class Solution {
public:
long long maxScore(vector<int>& nums, int x) {
int n = nums.size();
long long ans = nums[0];
vector<vector<long long>> dp(n, vector<long long>(2, -INF));
dp[0][nums[0] % 2] = nums[0];
for (int i = 1; i < n; i++) {
int p = nums[i] % 2;
dp[i][p] = max(dp[i - 1][p] + nums[i], dp[i - 1][1 - p] + nums[i] - x);
dp[i][1 - p] = dp[i - 1][1 - p];
ans = max(ans, dp[i][p]);
}
return ans;
}
};

寫法二:空間優化

注意到在寫法一中,dp[i][p]dp[i][p] 的值只和前一個狀態的 dp[i1][p]dp[i-1][p]dp[i1][1p]dp[i-1][1-p] 有關,且 dp[i][1p]dp[i][1-p] 的值在每一步都不變,因此可以對空間進行優化,只使用一個長度為 22 的陣列 ff 來存儲 dp[i][p]dp[i][p]dp[i][1p]dp[i][1-p] 的值。

複雜度分析

  • 時間複雜度:O(n)\mathcal{O}(n),其中 nn 是陣列 numsnums 的長度,只需要遍歷一次陣列。
  • 空間複雜度:O(1)\mathcal{O}(1),只使用了長度為 22 的陣列 ff
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def maxScore(self, nums: List[int], x: int) -> int:
n = len(nums)
ans = nums[0]
f = [float('-inf'), float('-inf')]
f[nums[0] % 2] = nums[0]
for i in range(1, n):
p = nums[i] % 2 # parity, 0: even, 1: odd
f[p] = max(f[p] + nums[i], f[1 - p] + nums[i] - x)
ans = max(ans, f[p])
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const int INF = 0x3f3f3f3f;
class Solution {
public:
long long maxScore(vector<int>& nums, int x) {
int n = nums.size();
long long ans = nums[0];
vector<long long> dp(2, -INF);
dp[nums[0] % 2] = nums[0];
for (int i = 1; i < n; i++) {
int p = nums[i] % 2;
dp[p] = max(dp[p] + nums[i], dp[1 - p] + nums[i] - x);
ans = max(ans, dp[p]);
}
return ans;
}
};

寫在最後

Cover photo is generated by @ゴリラの素材屋さん, thanks for their work!