mirror of
https://github.com/halfrost/LeetCode-Go.git
synced 2025-07-04 08:02:30 +08:00
Add solution 1674、1690
This commit is contained in:
@ -0,0 +1,34 @@
|
||||
package leetcode
|
||||
|
||||
func minMoves(nums []int, limit int) int {
|
||||
diff := make([]int, limit*2+2) // nums[i] <= limit, b+limit+1 is maximum limit+limit+1
|
||||
for j := 0; j < len(nums)/2; j++ {
|
||||
a, b := min(nums[j], nums[len(nums)-j-1]), max(nums[j], nums[len(nums)-j-1])
|
||||
// using prefix sum: most interesting point, and is the key to reduce complexity
|
||||
diff[2] += 2
|
||||
diff[a+1]--
|
||||
diff[a+b]--
|
||||
diff[a+b+1]++
|
||||
diff[b+limit+1]++
|
||||
}
|
||||
cur, res := 0, len(nums)
|
||||
for i := 2; i <= 2*limit; i++ {
|
||||
cur += diff[i]
|
||||
res = min(res, cur)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package leetcode
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type question1674 struct {
|
||||
para1674
|
||||
ans1674
|
||||
}
|
||||
|
||||
// para 是参数
|
||||
// one 代表第一个参数
|
||||
type para1674 struct {
|
||||
nums []int
|
||||
limit int
|
||||
}
|
||||
|
||||
// ans 是答案
|
||||
// one 代表第一个答案
|
||||
type ans1674 struct {
|
||||
one int
|
||||
}
|
||||
|
||||
func Test_Problem1674(t *testing.T) {
|
||||
|
||||
qs := []question1674{
|
||||
|
||||
{
|
||||
para1674{[]int{1, 2, 4, 3}, 4},
|
||||
ans1674{1},
|
||||
},
|
||||
|
||||
{
|
||||
para1674{[]int{1, 2, 2, 1}, 2},
|
||||
ans1674{2},
|
||||
},
|
||||
|
||||
{
|
||||
para1674{[]int{1, 2, 1, 2}, 2},
|
||||
ans1674{0},
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Printf("------------------------Leetcode Problem 1674------------------------\n")
|
||||
|
||||
for _, q := range qs {
|
||||
_, p := q.ans1674, q.para1674
|
||||
fmt.Printf("【input】:%v 【output】:%v\n", p, minMoves(p.nums, p.limit))
|
||||
}
|
||||
fmt.Printf("\n\n\n")
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
# [1674. Minimum Moves to Make Array Complementary](https://leetcode.com/problems/minimum-moves-to-make-array-complementary/)
|
||||
|
||||
## 题目
|
||||
|
||||
You are given an integer array `nums` of **even** length `n` and an integer `limit`. In one move, you can replace any integer from `nums` with another integer between `1` and `limit`, inclusive.
|
||||
|
||||
The array `nums` is **complementary** if for all indices `i` (**0-indexed**), `nums[i] + nums[n - 1 - i]` equals the same number. For example, the array `[1,2,3,4]` is complementary because for all indices `i`, `nums[i] + nums[n - 1 - i] = 5`.
|
||||
|
||||
Return the ***minimum** number of moves required to make* `nums` ***complementary***.
|
||||
|
||||
**Example 1:**
|
||||
|
||||
```
|
||||
Input: nums = [1,2,4,3], limit = 4
|
||||
Output: 1
|
||||
Explanation: In 1 move, you can change nums to [1,2,2,3] (underlined elements are changed).
|
||||
nums[0] + nums[3] = 1 + 3 = 4.
|
||||
nums[1] + nums[2] = 2 + 2 = 4.
|
||||
nums[2] + nums[1] = 2 + 2 = 4.
|
||||
nums[3] + nums[0] = 3 + 1 = 4.
|
||||
Therefore, nums[i] + nums[n-1-i] = 4 for every i, so nums is complementary.
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
|
||||
```
|
||||
Input: nums = [1,2,2,1], limit = 2
|
||||
Output: 2
|
||||
Explanation: In 2 moves, you can change nums to [2,2,2,2]. You cannot change any number to 3 since 3 > limit.
|
||||
```
|
||||
|
||||
**Example 3:**
|
||||
|
||||
```
|
||||
Input: nums = [1,2,1,2], limit = 2
|
||||
Output: 0
|
||||
Explanation: nums is already complementary.
|
||||
```
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- `n == nums.length`
|
||||
- `2 <= n <= 105`
|
||||
- `1 <= nums[i] <= limit <= 105`
|
||||
- `n` is even.
|
||||
|
||||
## 题目大意
|
||||
|
||||
给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。
|
||||
|
||||
如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。
|
||||
|
||||
返回使数组 互补 的 最少 操作次数。
|
||||
|
||||
## 解题思路
|
||||
|
||||
- 这一题考察的是差分数组。通过分析题意,可以得出,针对每一个 `sum` 的取值范围是 `[2, 2* limt]`,定义 `a = min(nums[i], nums[n - i - 1])`,`b = max(nums[i], nums[n - i - 1])`,在这个区间内,又可以细分成 5 个区间,`[2, a + 1)`,`[a + 1, a + b)`,`[a + b + 1, a + b + 1)`,`[a + b + 1, b + limit + 1)`,`[b + limit + 1, 2 * limit)`,在这 5 个区间内使得数组互补的最小操作次数分别是 `2(减少 a, 减少 b)`,`1(减少 b)`,`0(不用操作)`,`1(增大 a)`,`+2(增大 a, 增大 b)`,换个表达方式,按照扫描线从左往右扫描,在这 5 个区间内使得数组互补的最小操作次数叠加变化分别是 `+2(减少 a, 减少 b)`,`-1(减少 a)`,`-1(不用操作)`,`+1(增大 a)`,`+1(增大 a, 增大 b)`,利用这前后两个区间的关系,就可以构造一个差分数组。差分数组反应的是前后两者的关系。如果想求得 0 ~ n 的总关系,只需要求一次前缀和即可。
|
||||
- 这道题要求输出最少的操作次数,所以利用差分数组 + 前缀和,累加前缀和的同时维护最小值。从左往右扫描完一遍以后,输出最小值即可。
|
||||
|
||||
## 代码
|
||||
|
||||
```go
|
||||
package leetcode
|
||||
|
||||
func minMoves(nums []int, limit int) int {
|
||||
diff := make([]int, limit*2+2) // nums[i] <= limit, b+limit+1 is maximum limit+limit+1
|
||||
for j := 0; j < len(nums)/2; j++ {
|
||||
a, b := min(nums[j], nums[len(nums)-j-1]), max(nums[j], nums[len(nums)-j-1])
|
||||
// using prefix sum: most interesting point, and is the key to reduce complexity
|
||||
diff[2] += 2
|
||||
diff[a+1]--
|
||||
diff[a+b]--
|
||||
diff[a+b+1]++
|
||||
diff[b+limit+1]++
|
||||
}
|
||||
cur, res := 0, len(nums)
|
||||
for i := 2; i <= 2*limit; i++ {
|
||||
cur += diff[i]
|
||||
res = min(res, cur)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
65
leetcode/1690.Stone-Game-VII/1690. Stone Game VII.go
Normal file
65
leetcode/1690.Stone-Game-VII/1690. Stone Game VII.go
Normal file
@ -0,0 +1,65 @@
|
||||
package leetcode
|
||||
|
||||
// 解法一 优化空间版 DP
|
||||
func stoneGameVII(stones []int) int {
|
||||
n := len(stones)
|
||||
sum := make([]int, n)
|
||||
dp := make([]int, n)
|
||||
for i, d := range stones {
|
||||
sum[i] = d
|
||||
}
|
||||
for i := 1; i < n; i++ {
|
||||
for j := 0; j+i < n; j++ {
|
||||
if (n-i)%2 == 1 {
|
||||
d0 := dp[j] + sum[j]
|
||||
d1 := dp[j+1] + sum[j+1]
|
||||
if d0 > d1 {
|
||||
dp[j] = d0
|
||||
} else {
|
||||
dp[j] = d1
|
||||
}
|
||||
} else {
|
||||
d0 := dp[j] - sum[j]
|
||||
d1 := dp[j+1] - sum[j+1]
|
||||
if d0 < d1 {
|
||||
dp[j] = d0
|
||||
} else {
|
||||
dp[j] = d1
|
||||
}
|
||||
}
|
||||
sum[j] = sum[j] + stones[i+j]
|
||||
}
|
||||
}
|
||||
return dp[0]
|
||||
}
|
||||
|
||||
// 解法二 常规 DP
|
||||
func stoneGameVII1(stones []int) int {
|
||||
prefixSum := make([]int, len(stones))
|
||||
for i := 0; i < len(stones); i++ {
|
||||
if i == 0 {
|
||||
prefixSum[i] = stones[i]
|
||||
} else {
|
||||
prefixSum[i] = prefixSum[i-1] + stones[i]
|
||||
}
|
||||
}
|
||||
dp := make([][]int, len(stones))
|
||||
for i := range dp {
|
||||
dp[i] = make([]int, len(stones))
|
||||
dp[i][i] = 0
|
||||
}
|
||||
n := len(stones)
|
||||
for l := 2; l <= n; l++ {
|
||||
for i := 0; i+l <= n; i++ {
|
||||
dp[i][i+l-1] = max(prefixSum[i+l-1]-prefixSum[i+1]+stones[i+1]-dp[i+1][i+l-1], prefixSum[i+l-2]-prefixSum[i]+stones[i]-dp[i][i+l-2])
|
||||
}
|
||||
}
|
||||
return dp[0][n-1]
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
47
leetcode/1690.Stone-Game-VII/1690. Stone Game VII_test.go
Normal file
47
leetcode/1690.Stone-Game-VII/1690. Stone Game VII_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package leetcode
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type question1690 struct {
|
||||
para1690
|
||||
ans1690
|
||||
}
|
||||
|
||||
// para 是参数
|
||||
// one 代表第一个参数
|
||||
type para1690 struct {
|
||||
stones []int
|
||||
}
|
||||
|
||||
// ans 是答案
|
||||
// one 代表第一个答案
|
||||
type ans1690 struct {
|
||||
one int
|
||||
}
|
||||
|
||||
func Test_Problem1690(t *testing.T) {
|
||||
|
||||
qs := []question1690{
|
||||
|
||||
{
|
||||
para1690{[]int{5, 3, 1, 4, 2}},
|
||||
ans1690{6},
|
||||
},
|
||||
|
||||
{
|
||||
para1690{[]int{7, 90, 5, 1, 100, 10, 10, 2}},
|
||||
ans1690{122},
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Printf("------------------------Leetcode Problem 1690------------------------\n")
|
||||
|
||||
for _, q := range qs {
|
||||
_, p := q.ans1690, q.para1690
|
||||
fmt.Printf("【input】:%v 【output】:%v\n", p, stoneGameVII(p.stones))
|
||||
}
|
||||
fmt.Printf("\n\n\n")
|
||||
}
|
130
leetcode/1690.Stone-Game-VII/README.md
Normal file
130
leetcode/1690.Stone-Game-VII/README.md
Normal file
@ -0,0 +1,130 @@
|
||||
# [1690. Stone Game VII](https://leetcode.com/problems/stone-game-vii/)
|
||||
|
||||
## 题目
|
||||
|
||||
Alice and Bob take turns playing a game, with **Alice starting first**.
|
||||
|
||||
There are `n` stones arranged in a row. On each player's turn, they can **remove** either the leftmost stone or the rightmost stone from the row and receive points equal to the **sum** of the remaining stones' values in the row. The winner is the one with the higher score when there are no stones left to remove.
|
||||
|
||||
Bob found that he will always lose this game (poor Bob, he always loses), so he decided to **minimize the score's difference**. Alice's goal is to **maximize the difference** in the score.
|
||||
|
||||
Given an array of integers `stones` where `stones[i]` represents the value of the `ith` stone **from the left**, return *the **difference** in Alice and Bob's score if they both play **optimally**.*
|
||||
|
||||
**Example 1:**
|
||||
|
||||
```
|
||||
Input: stones = [5,3,1,4,2]
|
||||
Output: 6
|
||||
Explanation:
|
||||
- Alice removes 2 and gets 5 + 3 + 1 + 4 = 13 points. Alice = 13, Bob = 0, stones = [5,3,1,4].
|
||||
- Bob removes 5 and gets 3 + 1 + 4 = 8 points. Alice = 13, Bob = 8, stones = [3,1,4].
|
||||
- Alice removes 3 and gets 1 + 4 = 5 points. Alice = 18, Bob = 8, stones = [1,4].
|
||||
- Bob removes 1 and gets 4 points. Alice = 18, Bob = 12, stones = [4].
|
||||
- Alice removes 4 and gets 0 points. Alice = 18, Bob = 12, stones = [].
|
||||
The score difference is 18 - 12 = 6.
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
|
||||
```
|
||||
Input: stones = [7,90,5,1,100,10,10,2]
|
||||
Output: 122
|
||||
```
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- `n == stones.length`
|
||||
- `2 <= n <= 1000`
|
||||
- `1 <= stones[i] <= 1000`
|
||||
|
||||
## 题目大意
|
||||
|
||||
石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。有 n 块石子排成一排。每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,并获得与该行中剩余石头值之 和 相等的得分。当没有石头可移除时,得分较高者获胜。鲍勃发现他总是输掉游戏(可怜的鲍勃,他总是输),所以他决定尽力 减小得分的差值 。爱丽丝的目标是最大限度地 扩大得分的差值 。
|
||||
|
||||
给你一个整数数组 stones ,其中 stones[i] 表示 从左边开始 的第 i 个石头的值,如果爱丽丝和鲍勃都 发挥出最佳水平 ,请返回他们 得分的差值 。
|
||||
|
||||
## 解题思路
|
||||
|
||||
- 首先考虑 Bob 缩小分值差距意味着什么,意味着他想让他和 Alice 相对分数最小。Bob 已经明确肯定是输,所以他的分数一定比 Alice 小,那么 Bob - Alice 分数相减一定是负数。相对分数越小,意味着差值越大。负数越大,差值越小。-50 和 -10,-10 数值大,相差小。所以 Bob 的操作是让相对分数越大。Alice 的目的也是这样,要让 Alice - Bob 的相对分数越大,这里是正数的越大。综上,两者的目的相同,都是让相对分数最大化。
|
||||
- 定义 `dp[i][j]` 代表在当前 `stone[i ~ j]` 区间内能获得的最大分差。状态转移方程为:
|
||||
|
||||
```go
|
||||
dp[i][j] = max(
|
||||
sum(i + 1, j) - dp[i + 1][j], // 这一局取走 `stone[i]`,获得 sum(i + 1, j) 分数,再减去剩下对手能获得的分数,即是此局能获得的最大分差。
|
||||
sum(i, j - 1) - dp[i][j - 1] // 这一局取走 `stone[j]`,获得 sum(i, j - 1) 分数,再减去剩下对手能获得的分数,即是此局能获得的最大分差。
|
||||
)
|
||||
```
|
||||
|
||||
计算 `sum(i + 1, j) = stone[i + 1] + stone[i + 2] + …… + stone[j]` 利用前缀和计算区间和。
|
||||
|
||||
- 解法二是正常思路解答出来的代码。解法一是压缩了 DP 数组,在 DP 状态转移的时候,生成下一个 `dp[j]` 实际上是有规律的。利用这个规律可以少存一维数据,压缩空间。解法一的代码直接写出来,比较难想。先写出解法二的代码,然后找到递推规律,优化空间压缩一维,再写出解法一的代码。
|
||||
|
||||
## 代码
|
||||
|
||||
```go
|
||||
package leetcode
|
||||
|
||||
// 解法一 优化空间版 DP
|
||||
func stoneGameVII(stones []int) int {
|
||||
n := len(stones)
|
||||
sum := make([]int, n)
|
||||
dp := make([]int, n)
|
||||
for i, d := range stones {
|
||||
sum[i] = d
|
||||
}
|
||||
for i := 1; i < n; i++ {
|
||||
for j := 0; j+i < n; j++ {
|
||||
if (n-i)%2 == 1 {
|
||||
d0 := dp[j] + sum[j]
|
||||
d1 := dp[j+1] + sum[j+1]
|
||||
if d0 > d1 {
|
||||
dp[j] = d0
|
||||
} else {
|
||||
dp[j] = d1
|
||||
}
|
||||
} else {
|
||||
d0 := dp[j] - sum[j]
|
||||
d1 := dp[j+1] - sum[j+1]
|
||||
if d0 < d1 {
|
||||
dp[j] = d0
|
||||
} else {
|
||||
dp[j] = d1
|
||||
}
|
||||
}
|
||||
sum[j] = sum[j] + stones[i+j]
|
||||
}
|
||||
}
|
||||
return dp[0]
|
||||
}
|
||||
|
||||
// 解法二 常规 DP
|
||||
func stoneGameVII1(stones []int) int {
|
||||
prefixSum := make([]int, len(stones))
|
||||
for i := 0; i < len(stones); i++ {
|
||||
if i == 0 {
|
||||
prefixSum[i] = stones[i]
|
||||
} else {
|
||||
prefixSum[i] = prefixSum[i-1] + stones[i]
|
||||
}
|
||||
}
|
||||
dp := make([][]int, len(stones))
|
||||
for i := range dp {
|
||||
dp[i] = make([]int, len(stones))
|
||||
dp[i][i] = 0
|
||||
}
|
||||
n := len(stones)
|
||||
for l := 2; l <= n; l++ {
|
||||
for i := 0; i+l <= n; i++ {
|
||||
dp[i][i+l-1] = max(prefixSum[i+l-1]-prefixSum[i+1]+stones[i+1]-dp[i+1][i+l-1], prefixSum[i+l-2]-prefixSum[i]+stones[i]-dp[i][i+l-2])
|
||||
}
|
||||
}
|
||||
return dp[0][n-1]
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
@ -0,0 +1,97 @@
|
||||
# [1674. Minimum Moves to Make Array Complementary](https://leetcode.com/problems/minimum-moves-to-make-array-complementary/)
|
||||
|
||||
## 题目
|
||||
|
||||
You are given an integer array `nums` of **even** length `n` and an integer `limit`. In one move, you can replace any integer from `nums` with another integer between `1` and `limit`, inclusive.
|
||||
|
||||
The array `nums` is **complementary** if for all indices `i` (**0-indexed**), `nums[i] + nums[n - 1 - i]` equals the same number. For example, the array `[1,2,3,4]` is complementary because for all indices `i`, `nums[i] + nums[n - 1 - i] = 5`.
|
||||
|
||||
Return the ***minimum** number of moves required to make* `nums` ***complementary***.
|
||||
|
||||
**Example 1:**
|
||||
|
||||
```
|
||||
Input: nums = [1,2,4,3], limit = 4
|
||||
Output: 1
|
||||
Explanation: In 1 move, you can change nums to [1,2,2,3] (underlined elements are changed).
|
||||
nums[0] + nums[3] = 1 + 3 = 4.
|
||||
nums[1] + nums[2] = 2 + 2 = 4.
|
||||
nums[2] + nums[1] = 2 + 2 = 4.
|
||||
nums[3] + nums[0] = 3 + 1 = 4.
|
||||
Therefore, nums[i] + nums[n-1-i] = 4 for every i, so nums is complementary.
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
|
||||
```
|
||||
Input: nums = [1,2,2,1], limit = 2
|
||||
Output: 2
|
||||
Explanation: In 2 moves, you can change nums to [2,2,2,2]. You cannot change any number to 3 since 3 > limit.
|
||||
```
|
||||
|
||||
**Example 3:**
|
||||
|
||||
```
|
||||
Input: nums = [1,2,1,2], limit = 2
|
||||
Output: 0
|
||||
Explanation: nums is already complementary.
|
||||
```
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- `n == nums.length`
|
||||
- `2 <= n <= 105`
|
||||
- `1 <= nums[i] <= limit <= 105`
|
||||
- `n` is even.
|
||||
|
||||
## 题目大意
|
||||
|
||||
给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。
|
||||
|
||||
如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。
|
||||
|
||||
返回使数组 互补 的 最少 操作次数。
|
||||
|
||||
## 解题思路
|
||||
|
||||
- 这一题考察的是差分数组。通过分析题意,可以得出,针对每一个 `sum` 的取值范围是 `[2, 2* limt]`,定义 `a = min(nums[i], nums[n - i - 1])`,`b = max(nums[i], nums[n - i - 1])`,在这个区间内,又可以细分成 5 个区间,`[2, a + 1)`,`[a + 1, a + b)`,`[a + b + 1, a + b + 1)`,`[a + b + 1, b + limit + 1)`,`[b + limit + 1, 2 * limit)`,在这 5 个区间内使得数组互补的最小操作次数分别是 `2(减少 a, 减少 b)`,`1(减少 b)`,`0(不用操作)`,`1(增大 a)`,`+2(增大 a, 增大 b)`,换个表达方式,按照扫描线从左往右扫描,在这 5 个区间内使得数组互补的最小操作次数叠加变化分别是 `+2(减少 a, 减少 b)`,`-1(减少 a)`,`-1(不用操作)`,`+1(增大 a)`,`+1(增大 a, 增大 b)`,利用这前后两个区间的关系,就可以构造一个差分数组。差分数组反应的是前后两者的关系。如果想求得 0 ~ n 的总关系,只需要求一次前缀和即可。
|
||||
- 这道题要求输出最少的操作次数,所以利用差分数组 + 前缀和,累加前缀和的同时维护最小值。从左往右扫描完一遍以后,输出最小值即可。
|
||||
|
||||
## 代码
|
||||
|
||||
```go
|
||||
package leetcode
|
||||
|
||||
func minMoves(nums []int, limit int) int {
|
||||
diff := make([]int, limit*2+2) // nums[i] <= limit, b+limit+1 is maximum limit+limit+1
|
||||
for j := 0; j < len(nums)/2; j++ {
|
||||
a, b := min(nums[j], nums[len(nums)-j-1]), max(nums[j], nums[len(nums)-j-1])
|
||||
// using prefix sum: most interesting point, and is the key to reduce complexity
|
||||
diff[2] += 2
|
||||
diff[a+1]--
|
||||
diff[a+b]--
|
||||
diff[a+b+1]++
|
||||
diff[b+limit+1]++
|
||||
}
|
||||
cur, res := 0, len(nums)
|
||||
for i := 2; i <= 2*limit; i++ {
|
||||
cur += diff[i]
|
||||
res = min(res, cur)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
130
website/content/ChapterFour/1690.Stone-Game-VII.md
Normal file
130
website/content/ChapterFour/1690.Stone-Game-VII.md
Normal file
@ -0,0 +1,130 @@
|
||||
# [1690. Stone Game VII](https://leetcode.com/problems/stone-game-vii/)
|
||||
|
||||
## 题目
|
||||
|
||||
Alice and Bob take turns playing a game, with **Alice starting first**.
|
||||
|
||||
There are `n` stones arranged in a row. On each player's turn, they can **remove** either the leftmost stone or the rightmost stone from the row and receive points equal to the **sum** of the remaining stones' values in the row. The winner is the one with the higher score when there are no stones left to remove.
|
||||
|
||||
Bob found that he will always lose this game (poor Bob, he always loses), so he decided to **minimize the score's difference**. Alice's goal is to **maximize the difference** in the score.
|
||||
|
||||
Given an array of integers `stones` where `stones[i]` represents the value of the `ith` stone **from the left**, return *the **difference** in Alice and Bob's score if they both play **optimally**.*
|
||||
|
||||
**Example 1:**
|
||||
|
||||
```
|
||||
Input: stones = [5,3,1,4,2]
|
||||
Output: 6
|
||||
Explanation:
|
||||
- Alice removes 2 and gets 5 + 3 + 1 + 4 = 13 points. Alice = 13, Bob = 0, stones = [5,3,1,4].
|
||||
- Bob removes 5 and gets 3 + 1 + 4 = 8 points. Alice = 13, Bob = 8, stones = [3,1,4].
|
||||
- Alice removes 3 and gets 1 + 4 = 5 points. Alice = 18, Bob = 8, stones = [1,4].
|
||||
- Bob removes 1 and gets 4 points. Alice = 18, Bob = 12, stones = [4].
|
||||
- Alice removes 4 and gets 0 points. Alice = 18, Bob = 12, stones = [].
|
||||
The score difference is 18 - 12 = 6.
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
|
||||
```
|
||||
Input: stones = [7,90,5,1,100,10,10,2]
|
||||
Output: 122
|
||||
```
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- `n == stones.length`
|
||||
- `2 <= n <= 1000`
|
||||
- `1 <= stones[i] <= 1000`
|
||||
|
||||
## 题目大意
|
||||
|
||||
石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。有 n 块石子排成一排。每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,并获得与该行中剩余石头值之 和 相等的得分。当没有石头可移除时,得分较高者获胜。鲍勃发现他总是输掉游戏(可怜的鲍勃,他总是输),所以他决定尽力 减小得分的差值 。爱丽丝的目标是最大限度地 扩大得分的差值 。
|
||||
|
||||
给你一个整数数组 stones ,其中 stones[i] 表示 从左边开始 的第 i 个石头的值,如果爱丽丝和鲍勃都 发挥出最佳水平 ,请返回他们 得分的差值 。
|
||||
|
||||
## 解题思路
|
||||
|
||||
- 首先考虑 Bob 缩小分值差距意味着什么,意味着他想让他和 Alice 相对分数最小。Bob 已经明确肯定是输,所以他的分数一定比 Alice 小,那么 Bob - Alice 分数相减一定是负数。相对分数越小,意味着差值越大。负数越大,差值越小。-50 和 -10,-10 数值大,相差小。所以 Bob 的操作是让相对分数越大。Alice 的目的也是这样,要让 Alice - Bob 的相对分数越大,这里是正数的越大。综上,两者的目的相同,都是让相对分数最大化。
|
||||
- 定义 `dp[i][j]` 代表在当前 `stone[i ~ j]` 区间内能获得的最大分差。状态转移方程为:
|
||||
|
||||
```go
|
||||
dp[i][j] = max(
|
||||
sum(i + 1, j) - dp[i + 1][j], // 这一局取走 `stone[i]`,获得 sum(i + 1, j) 分数,再减去剩下对手能获得的分数,即是此局能获得的最大分差。
|
||||
sum(i, j - 1) - dp[i][j - 1] // 这一局取走 `stone[j]`,获得 sum(i, j - 1) 分数,再减去剩下对手能获得的分数,即是此局能获得的最大分差。
|
||||
)
|
||||
```
|
||||
|
||||
计算 `sum(i + 1, j) = stone[i + 1] + stone[i + 2] + …… + stone[j]` 利用前缀和计算区间和。
|
||||
|
||||
- 解法二是正常思路解答出来的代码。解法一是压缩了 DP 数组,在 DP 状态转移的时候,生成下一个 `dp[j]` 实际上是有规律的。利用这个规律可以少存一维数据,压缩空间。解法一的代码直接写出来,比较难想。先写出解法二的代码,然后找到递推规律,优化空间压缩一维,再写出解法一的代码。
|
||||
|
||||
## 代码
|
||||
|
||||
```go
|
||||
package leetcode
|
||||
|
||||
// 解法一 优化空间版 DP
|
||||
func stoneGameVII(stones []int) int {
|
||||
n := len(stones)
|
||||
sum := make([]int, n)
|
||||
dp := make([]int, n)
|
||||
for i, d := range stones {
|
||||
sum[i] = d
|
||||
}
|
||||
for i := 1; i < n; i++ {
|
||||
for j := 0; j+i < n; j++ {
|
||||
if (n-i)%2 == 1 {
|
||||
d0 := dp[j] + sum[j]
|
||||
d1 := dp[j+1] + sum[j+1]
|
||||
if d0 > d1 {
|
||||
dp[j] = d0
|
||||
} else {
|
||||
dp[j] = d1
|
||||
}
|
||||
} else {
|
||||
d0 := dp[j] - sum[j]
|
||||
d1 := dp[j+1] - sum[j+1]
|
||||
if d0 < d1 {
|
||||
dp[j] = d0
|
||||
} else {
|
||||
dp[j] = d1
|
||||
}
|
||||
}
|
||||
sum[j] = sum[j] + stones[i+j]
|
||||
}
|
||||
}
|
||||
return dp[0]
|
||||
}
|
||||
|
||||
// 解法二 常规 DP
|
||||
func stoneGameVII1(stones []int) int {
|
||||
prefixSum := make([]int, len(stones))
|
||||
for i := 0; i < len(stones); i++ {
|
||||
if i == 0 {
|
||||
prefixSum[i] = stones[i]
|
||||
} else {
|
||||
prefixSum[i] = prefixSum[i-1] + stones[i]
|
||||
}
|
||||
}
|
||||
dp := make([][]int, len(stones))
|
||||
for i := range dp {
|
||||
dp[i] = make([]int, len(stones))
|
||||
dp[i][i] = 0
|
||||
}
|
||||
n := len(stones)
|
||||
for l := 2; l <= n; l++ {
|
||||
for i := 0; i+l <= n; i++ {
|
||||
dp[i][i+l-1] = max(prefixSum[i+l-1]-prefixSum[i+1]+stones[i+1]-dp[i+1][i+l-1], prefixSum[i+l-2]-prefixSum[i]+stones[i]-dp[i][i+l-2])
|
||||
}
|
||||
}
|
||||
return dp[0][n-1]
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
@ -570,6 +570,7 @@ headless: true
|
||||
- [1670.Design-Front-Middle-Back-Queue]({{< relref "/ChapterFour/1670.Design-Front-Middle-Back-Queue.md" >}})
|
||||
- [1672.Richest-Customer-Wealth]({{< relref "/ChapterFour/1672.Richest-Customer-Wealth.md" >}})
|
||||
- [1673.Find-the-Most-Competitive-Subsequence]({{< relref "/ChapterFour/1673.Find-the-Most-Competitive-Subsequence.md" >}})
|
||||
- [1674.Minimum-Moves-to-Make-Array-Complementary]({{< relref "/ChapterFour/1674.Minimum-Moves-to-Make-Array-Complementary.md" >}})
|
||||
- [1678.Goal-Parser-Interpretation]({{< relref "/ChapterFour/1678.Goal-Parser-Interpretation.md" >}})
|
||||
- [1679.Max-Number-of-K-Sum-Pairs]({{< relref "/ChapterFour/1679.Max-Number-of-K-Sum-Pairs.md" >}})
|
||||
- [1680.Concatenation-of-Consecutive-Binary-Numbers]({{< relref "/ChapterFour/1680.Concatenation-of-Consecutive-Binary-Numbers.md" >}})
|
||||
@ -578,4 +579,5 @@ headless: true
|
||||
- [1685.Sum-of-Absolute-Differences-in-a-Sorted-Array]({{< relref "/ChapterFour/1685.Sum-of-Absolute-Differences-in-a-Sorted-Array.md" >}})
|
||||
- [1688.Count-of-Matches-in-Tournament]({{< relref "/ChapterFour/1688.Count-of-Matches-in-Tournament.md" >}})
|
||||
- [1689.Partitioning-Into-Minimum-Number-Of-Deci-Binary-Numbers]({{< relref "/ChapterFour/1689.Partitioning-Into-Minimum-Number-Of-Deci-Binary-Numbers.md" >}})
|
||||
- [1690.Stone-Game-VII]({{< relref "/ChapterFour/1690.Stone-Game-VII.md" >}})
|
||||
<br />
|
||||
|
Reference in New Issue
Block a user