Description
給定球員的分數
scores和年齡ages,選出一支隊伍使總分最高,但不能有衝突:年齡較小的球員分數不能嚴格高於年齡較大的球員。
Example:
Input: scores = [4,5,6,5], ages = [2,1,2,1] Output: 16
Intuition#
按年齡(及分數)排序後,問題轉化為求分數的最長非遞減子序列的最大和(LIS 變體)。
- 排序後,只需確保選出的球員分數也是非遞減的
- 等同於 LIS 問題,但目標是最大化分數總和而非長度
dp[i]= 以第i個球員結尾的最大總分
Approaches#
1: DP O(n^2)#
- 概念: 排序後對每個球員,找前面所有分數 <= 它的球員,取最大總分
- 時間複雜度:
O(n^2) - 空間複雜度:
O(n)
class Solution {
fun bestTeamScore(scores: IntArray, ages: IntArray): Int {
val n = scores.size
val players = Array(n) { intArrayOf(ages[it], scores[it]) }
players.sortWith(compareBy({ it[0] }, { it[1] }))
val dp = IntArray(n)
for (i in 0 until n) {
dp[i] = players[i][1]
for (j in 0 until i) {
if (players[j][1] <= players[i][1]) {
dp[i] = maxOf(dp[i], dp[j] + players[i][1])
}
}
}
return dp.max()
}
}⭐2: BIT (Binary Indexed Tree) Optimization#
- 概念: 用 BIT 在分數值域上維護最大 dp 值,將查詢從 O(n) 降到 O(log M)
- 時間複雜度:
O(n log M)其中 M 為分數最大值 - 空間複雜度:
O(M)
class Solution {
fun bestTeamScore(scores: IntArray, ages: IntArray): Int {
val n = scores.size
val players = Array(n) { intArrayOf(ages[it], scores[it]) }
players.sortWith(compareBy({ it[0] }, { it[1] }))
val maxScore = scores.max()
val bit = IntArray(maxScore + 2) // BIT,支援 prefix max query
fun query(i: Int): Int {
var idx = i; var res = 0
while (idx > 0) { res = maxOf(res, bit[idx]); idx -= idx and (-idx) }
return res
}
fun update(i: Int, v: Int) {
var idx = i
while (idx <= maxScore + 1) { bit[idx] = maxOf(bit[idx], v); idx += idx and (-idx) }
}
var ans = 0
for (p in players) {
val score = p[1]
val best = query(score + 1) + score // +1 因為 BIT 是 1-indexed
update(score + 1, best)
ans = maxOf(ans, best)
}
return ans
}
}🔑 Takeaways#
- Pattern: 排序 + LIS 變體 — 多維度約束問題,排序消除一個維度後轉化為 LIS
- 關鍵技巧: 按 (age, score) 排序後,只需保證分數非遞減即可避免衝突;BIT 可以將 LIS 類問題從 O(n^2) 優化到 O(n log n)