← 返回博客列表
Microsoft

微軟OA真題複盤:最大累積得分遊戲 + 最少操作次數

2026-03-17

Microsoft OA Question 1

題目一:最大累積得分遊戲

問題描述

一個遊戲規則如下:

求:到達終點的最大可能得分

解題思路

第一步:預處理素數

首先需要找出所有個位數為3的素數。這些素數是:3、13、23、43、53、73、83等。

def get_primes_ending_with_3(max_n):
    """獲取所有不超過max_n且個位數為3的素數"""
    primes = []
    
    def is_prime(num):
        if num < 2:
            return False
        if num == 2:
            return True
        if num % 2 == 0:
            return False
        for i in range(3, int(num**0.5) + 1, 2):
            if num % i == 0:
                return False
        return True
    
    for num in range(3, max_n, 10):  # 個位數為3的數:3, 13, 23, 33...
        if is_prime(num):
            primes.append(num)
    
    return primes

第二步:動態規劃求解

使用DP數組,其中dp[i]表示到達第i個格子的最大得分。

關鍵思路

def max_score_game(cells, n):
    """
    求最大累積得分
    
    Args:
        cells: 格子分值數組
        n: 格子總數
    
    Returns:
        到達終點的最大得分
    """
    # 預處理素數
    primes = get_primes_ending_with_3(n)
    
    # DP初始化
    dp = [-float('inf')] * n
    dp[0] = cells[0]  # 起點分值為0
    
    # 狀態轉移
    for i in range(n):
        if dp[i] == -float('inf'):
            continue  # 不可達的位置跳過
        
        # 嘗試移動1步
        if i + 1 < n:
            dp[i + 1] = max(dp[i + 1], dp[i] + cells[i + 1])
        
        # 嘗試移動p步(p為個位數為3的素數)
        for p in primes:
            if i + p < n:
                dp[i + p] = max(dp[i + p], dp[i] + cells[i + p])
            else:
                break  # 後續素數更大,不需要繼續
    
    return dp[n - 1]

第三步:示例演算

以題目給定的例子:cells = [0, -10, -20, -30, 50],n = 5

最終答案:40

時間複雜度分析

常見陷阱

陷阱1:忘記初始化為負無窮,導致不可達位置被錯誤計算 ✅ 解決:明確區分可達和不可達狀態

陷阱2:素數預處理不完整,遺漏某些素數 ✅ 解決:使用篩法或逐一檢驗,確保所有個位數為3的素數都被找到

陷阱3:沒有考慮負數分值的情況 ✅ 解決:DP初始化為負無窮,正確處理所有分值


題目二:最少操作次數

Microsoft OA Question 2

問題描述

給定一個正整數n,每次操作可以:

求:將n變為0的最少操作次數

解題思路

核心觀察:二進制貪心

這道題的關鍵在於觀察n的二進制表示,並利用二進制進位的性質。

關鍵規律

詳細算法

def min_operations(n):
    """
    求將n變為0的最少操作次數
    
    Args:
        n: 正整數
    
    Returns:
        最少操作次數
    """
    operations = 0
    
    while n > 0:
        # 觀察n的最後兩位二進制
        last_two_bits = n & 3  # 取最後兩位:n % 4
        
        if last_two_bits == 0:
            # 00:直接右移
            n >>= 1
        elif last_two_bits == 1:
            # 01:減1(變成00),然後右移
            n -= 1
            operations += 1
        elif last_two_bits == 2:
            # 10:直接右移
            n >>= 1
        else:  # last_two_bits == 3
            # 11:加1(進位變成100),然後右移
            n += 1
            operations += 1
        
        operations += 1  # 右移操作計數
    
    return operations

算法解釋

為什麼這樣做?

觀察二進制的最後兩位:

  1. 00:最後一位是0,直接右移即可
  2. 01:最後一位是1,需要減1變成00,然後右移
  3. 10:最後一位是0,直接右移即可
  4. 11:最後兩位都是1,加1會產生進位(變成100),然後右移

這樣做的好處是:

示例演算

以n = 5為例:

n = 5 = 101₂

第1步:最後兩位是01
  - 減1:5 - 1 = 4 = 100₂(操作1)
  - 右移:4 >> 1 = 2 = 10₂(操作2)

第2步:最後兩位是10
  - 右移:2 >> 1 = 1 = 1₂(操作3)

第3步:最後兩位是01
  - 減1:1 - 1 = 0(操作4)
  - 右移:0 >> 1 = 0(操作5)

總操作數:5

驗證:5 → 4 → 2 → 1 → 0
     (減1) (÷2) (÷2) (減1)

等等,讓我重新計算。題目說的是"操作",每次操作是加或減2^i。

重新理解題意

實際上,我們需要重新理解這個問題:

更優的思路:這等價於用最少個數的2的冪次來表示n。

例如:

所以最優是2個操作。

改進的算法

def min_operations_v2(n):
    """
    求將n變為0的最少操作次數(改進版)
    
    使用二進制表示的1的個數作為基礎
    """
    operations = 0
    
    while n > 0:
        last_two_bits = n & 3
        
        if last_two_bits == 0:
            # 00:直接右移
            n >>= 1
        elif last_two_bits == 1:
            # 01:減1
            n -= 1
            operations += 1
        elif last_two_bits == 2:
            # 10:直接右移
            n >>= 1
        else:  # last_two_bits == 3
            # 11:加1(進位)
            n += 1
            operations += 1
    
    return operations

對於n = 5:

5 = 101₂
最後兩位是01 → 減1 → 4 = 100₂(操作1)
最後兩位是00 → 右移 → 2 = 10₂
最後兩位是10 → 右移 → 1 = 1₂
最後兩位是01 → 減1 → 0(操作2)

總操作數:2 ✓

時間複雜度分析

常見陷阱

陷阱1:混淆"操作次數"和"右移次數" ✅ 解決:只有加或減才算操作,右移是免費的

陷阱2:沒有利用進位的性質 ✅ 解決:當最後兩位是11時,加1會產生進位,可以合併多個1

陷阱3:貪心策略不正確 ✅ 解決:觀察最後兩位,按規律處理


面試應對策略

時間管理

題目一(最大累積得分)

題目二(最少操作次數)

臨場技巧

  1. 題目一

    • 先確認素數的定義(個位數為3)
    • 明確邊界條件(不能超過n-1)
    • 從簡單例子開始推導
  2. 題目二

    • 先用幾個例子找規律
    • 觀察二進制表示
    • 解釋為什麼加1或減1是最優的

常見問題

Q:如果n很大(接近2^60),算法還能工作嗎?

A:可以。題目二的時間複雜度是O(log n),即使n = 2^60也只需要約60次迭代。題目一需要預處理素數,但個位數為3的素數密度足夠大,不會成為瓶頸。

Q:題目一中,如果所有格子分值都是負數怎麼辦?

A:仍然需要到達終點,所以選擇分值最小的路徑。DP算法會自動處理這種情況。

Q:能否用其他方法解題目二?

A:可以。題目二本質上是求用最少個數的2的冪次表示n。也可以用BFS或記憶化搜索,但二進制貪心是最優的。


完整代碼實現

def get_primes_ending_with_3(max_n):
    """獲取所有不超過max_n且個位數為3的素數"""
    primes = []
    
    def is_prime(num):
        if num < 2:
            return False
        if num == 2:
            return True
        if num % 2 == 0:
            return False
        for i in range(3, int(num**0.5) + 1, 2):
            if num % i == 0:
                return False
        return True
    
    for num in range(3, max_n, 10):
        if is_prime(num):
            primes.append(num)
    
    return primes


def max_score_game(cells):
    """求最大累積得分"""
    n = len(cells)
    primes = get_primes_ending_with_3(n)
    
    dp = [-float('inf')] * n
    dp[0] = cells[0]
    
    for i in range(n):
        if dp[i] == -float('inf'):
            continue
        
        # 移動1步
        if i + 1 < n:
            dp[i + 1] = max(dp[i + 1], dp[i] + cells[i + 1])
        
        # 移動p步
        for p in primes:
            if i + p < n:
                dp[i + p] = max(dp[i + p], dp[i] + cells[i + p])
            else:
                break
    
    return dp[n - 1]


def min_operations(n):
    """求最少操作次數"""
    operations = 0
    
    while n > 0:
        last_two_bits = n & 3
        
        if last_two_bits == 0:
            n >>= 1
        elif last_two_bits == 1:
            n -= 1
            operations += 1
        elif last_two_bits == 2:
            n >>= 1
        else:  # last_two_bits == 3
            n += 1
            operations += 1
    
    return operations


# 測試
if __name__ == "__main__":
    # 測試題目一
    cells = [0, -10, -20, -30, 50]
    print(f"最大得分: {max_score_game(cells)}")  # 輸出: 40
    
    # 測試題目二
    print(f"最少操作次數: {min_operations(5)}")  # 輸出: 2

🚀 需要面試輔助?

oavoservice 提供專業的 OA/VO 輔助服務,幫助你快速通過微軟、亞馬遜等大廠的技術面試。

👉 立即添加微信:Coding0201