周賽333。這題原本標的是難度是easy,搞得一堆人心裡崩潰,一點都不easy。

題目

輸入正整數n,你可以進行以下動作數次:

  • 對n增加或是減去2的冪次數

求使得n變成0所需的最小動作次數

解法

說到2的冪次數,這些數字必定只有一個1位元。可以理解為對某個位置增加/減少1位元。

從例題1的39來研究:

n = 39, 二進位 = 100111
加上1,得到101000
減掉1000,得到100000
減掉100000,得到0

發現單獨的1只能直接減掉,而連續出現的1可以先透過一次增加使之連續進位,最後變成一個1。
而每個進位後只會影響左方的位元,因此從最小的位元(LSB)開始向左遍歷,若找到連續的1位元則使之進位;單獨的1位元則直接刪除。

時間複雜度O(log n)。空間複雜度O(1)。

class Solution:
    def minOperations(self, n: int) -> int:
        ans=0
        
        for i in range(20):
            if n&(1<<i):
                ans+=1
                if n&(1<<(i+1)):
                    n+=(1<<i)
                else:
                    n-=(1<<i)
            
        return ans

上述方法可以使用lowbit技巧優化,用O(1)時間找到最後方的1位元。
有時候會比較快,但複雜度不變。

class Solution:
    def minOperations(self, n: int) -> int:
        ans=0
        while n>0:
            lb=n&(-n)
            if n&(lb<<1):
                n+=lb
            else:
                n-=lb
            ans+=1
        
        return ans