Maximum Subsequence Score

題目描述

給定兩個長度為 n 的整數陣列 nums1nums2,以及一個正整數 k。從索引中選出 k 個不同的索引 i1, i2, ..., ik,分數定義為 (nums1[i1] + nums1[i2] + ... + nums1[ik]) * min(nums2[i1], nums2[i2], ..., nums2[ik])。回傳最大可能分數。

輸入:nums1 = [1,3,3,2], nums2 = [2,1,3,4], k = 3
輸出:12
解釋:選索引 0, 2, 3 → (1+3+2) * min(2,3,4) = 6*2 = 12

解題思路

1

配對排序

將 (nums2[i], nums1[i]) 配對,按 nums2 降序排列。例如 nums1=[1,3,3,2], nums2=[2,1,3,4] → 排序後配對為 (4,2),(3,3),(2,1),(1,3)

2

遍歷配對

依序遍歷排序後的配對,因為 nums2 遞減,當前的 nums2 值一定是已遍歷中最小的,直接作為 min 使用

3

維護 Min Heap

用大小為 K 的 Min Heap 追蹤最大的 K 個 nums1 值。堆頂是最小值,方便判斷是否該替換

4

堆滿時計算

堆中元素達 K 個時,計算 sum × 當前 nums2 並與目前最佳答案比較,取較大值

5

替換最小值

堆已滿時加入新元素會讓堆大小超過 K,彈出堆頂(最小的 nums1)以維持 K 個最大值

程式碼

C#

csharp
public class Solution {
    public long MaxScore(int[] nums1, int[] nums2, int k) {
        int n = nums1.Length;
        // 按 nums2 降序排列
        var indices = Enumerable.Range(0, n)
            .OrderByDescending(i => nums2[i]).ToArray();
        
        var minHeap = new PriorityQueue<int, int>();
        long sum = 0, result = 0;
        
        foreach (int i in indices) {
            sum += nums1[i];
            minHeap.Enqueue(nums1[i], nums1[i]);
            
            // 堆超過 k 個,移除最小的
            if (minHeap.Count > k) {
                sum -= minHeap.Dequeue();
            }
            
            // 剛好 k 個時計算分數
            if (minHeap.Count == k) {
                result = Math.Max(result, sum * nums2[i]);
            }
        }
        return result;
    }
}

TypeScript

typescript
function maxScore(nums1: number[], nums2: number[], k: number): number {
    const n = nums1.length;
    // 按 nums2 降序排列索引
    const indices = Array.from({length: n}, (_, i) => i)
        .sort((a, b) => nums2[b] - nums2[a]);
    
    // 簡易 Min Heap(用排序陣列模擬)
    const heap: number[] = [];
    let sum = 0, result = 0;
    
    for (const i of indices) {
        sum += nums1[i];
        // 插入並維護排序
        heap.push(nums1[i]);
        heap.sort((a, b) => a - b);
        
        if (heap.length > k) {
            sum -= heap.shift()!; // 移除最小
        }
        if (heap.length === k) {
            result = Math.max(result, sum * nums2[i]);
        }
    }
    return result;
}

Python

python
import heapq
 
def maxScore(nums1: list[int], nums2: list[int], k: int) -> int:
    # 按 nums2 降序排列配對
    pairs = sorted(zip(nums2, nums1), reverse=True)
    
    heap = []  # min heap 維護最大的 k 個 nums1
    total = 0
    result = 0
    
    for n2, n1 in pairs:
        total += n1
        heapq.heappush(heap, n1)
        
        if len(heap) > k:
            total -= heapq.heappop(heap)  # 移除最小
        
        if len(heap) == k:
            result = max(result, total * n2)  # n2 是當前最小的 nums2
    
    return result

複雜度分析

| | Time | Space | |--|------|-------| | 本解法 | O(n log n) | O(n) |

圖解

按 nums2 降序排序
排序後
依序遍歷每個配對
加入 nums1
Min Heap (size K)
堆滿 K 個
sum × min(nums2) = score
更新答案
取最大 score

圖表展示了核心流程:先按 nums2 降序排列保證遍歷時 nums2 值遞減,再用 Min Heap 動態維護最大的 K 個 nums1 值。 每次堆滿 K 個時,當前 nums2 就是所選元素中的最小值,直接計算分數並更新答案。

進階觀點

💡面試追問

  • 如果 K 不固定,要求所有可能 K 值的最大分數怎麼辦? 在遍歷過程中,每當 heap 大小增加到新的 K 值時就記錄一次 sum × 當前 nums2,最後取所有 K 值對應答案的最大值。時間複雜度不變,仍為 O(n log n)。
  • 這題和 1383. Maximum Performance of a Team 的關係? 兩題框架幾乎相同:排序 + Min Heap。1383 取 sum + min 而非 sum × min,但貪心邏輯一致——按「min 來源」降序遍歷,用 heap 維護最佳的前 K 個值。
  • 為什麼按 nums2 降序排列? 降序遍歷保證當前 nums2 值一定 ≤ 所有已遍歷的 nums2 值,所以它自動就是被選中元素的 min(nums2),不需要額外的資料結構追蹤最小值。

邊界情況

理解測驗

🤔 為什麼要按 nums2 降序排列後遍歷?

🤔 Min Heap 在這題中的作用是什麼?

你可能也想看

2336. Smallest Number in Infinite Set2462. Total Cost to Hire K Workers

按 ← → 鍵切換課程