biweekly contest 145。
看到不少人說題目有瑕疵,沒有提及前導零
但個人覺得沒差,因為整數修改修出前導 0 後會損失數位個數,不可能加回來,不影響答案。

題目

輸入整數 n 和 m,兩者具有相同的數位個數。

你可以執行以下操作任意次:

  • 選擇 n 的任意非 9 數位,並將其增加 1。
  • 選擇 n 的任意非 0 數位,並將其減少 1。

n 在整個過程中都不可以是質數,包括初始值以及最終結果。

將 n 進行數字若干次操作的成本為變化過程的所有值之和。

求將 n 變成 m 的最小成本。若不可能則回傳 -1。

解法

把所有非質數當成圖上的節點,問題轉換成:

求 n 到 m 的最小路徑成本

相似題 752. Open the Lock


總之先預處理質數表,以供 O(1) 判斷。

求最短路問題,因為邊權不同,需使用 dijkstra。
從 n 出發,初始成本為 n。 枚舉當前節點 curr 的鄰居 adj,移動到 adj 的成本為 adj。

圖中至多有 O(N) 個頂點。
一個頂點至多 log N 位數,也就是 E = 2 log N 條邊。

時間複雜度 O(E log E)。 空間複雜度 O(E)。

def prime_table(n):
    sieve = [True] * (n + 1)
    sieve[0] = sieve[1] = False
    for i in range(2, int(n ** 0.5) + 1):
        if sieve[i]:
            for j in range(i * i, n + 1, i):
                sieve[j] = False
    return sieve

@cache
def neighbors(x):
    cand = []
    t = x
    mult = 1
    while t > 0:
        r = t % 10
        if r < 9:
            cand.append(x+mult)
        if r > 0 and (t >= 10 or r != 1):
            cand.append(x-mult)
        t //= 10
        mult *= 10
    return cand

is_prime = prime_table(10000+5)

class Solution:
    def minOperations(self, n: int, m: int) -> int:
        if is_prime[n] or is_prime[m]:
            return -1

        vis = set()
        vis.add(n)
        h = []
        heappush(h, [n, n]) # [cost, curr]
        while h:
            cost, curr = heappop(h)
            if curr == m:
                return cost
                
            for adj in neighbors(curr):
                if adj not in vis and not is_prime[adj]:
                    vis.add(adj)
                    heappush(h, [cost+adj, adj])

        return -1