Notes & software courses · Free to learn
Aph's Blog
On this page

ข้อ 69 · LC1318 Minimum Flips to Make a OR b Equal to c (พลิกบิตน้อยสุด) 🟡

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

หาจำนวน bit น้อยสุดที่ต้องพลิกใน a และ b เพื่อให้ (a OR b) เท่ากับ c โดยพิจารณาทีละ bit

โจทย์ (LC1318): กำหนดจำนวนเต็มบวกสามตัว a, b และ c ให้หาจำนวนครั้งน้อยที่สุดที่ต้องพลิก (flip) bit ของ a และ b รวมกัน เพื่อให้ (a OR b) เท่ากับ c พอดี การพลิกหนึ่งครั้งคือเปลี่ยน bit ใด ๆ จาก 0 เป็น 1 หรือจาก 1 เป็น 0

Example 1
Input:
a = 2 (0b010), b = 6 (0b110), c = 5 (0b101)
Output:
3
Explanation:
ต้องพลิกจน a=1 (0b001), b=4 (0b100) จึงจะได้ a OR b = 5 พอดี
Example 2
Input:
a = 4, b = 2, c = 6
Output:
0
Explanation:
4 OR 2 = 6 อยู่แล้ว ไม่ต้องพลิกเลย
Example 3
Input:
a = 1, b = 2, c = 3
Output:
0
Explanation:
1 OR 2 = 3 อยู่แล้ว ไม่ต้องพลิกเลย
Example 4
Input:
a = 4, b = 2, c = 7
Output:
1
Explanation:
4 OR 2 = 6 (0b110) ขาด bit ขวาสุด พลิก a หรือ b ให้ bit ขวาสุดเป็น 1 แค่ครั้งเดียวก็ได้ 7 (0b111)
Constraints (ข้อจำกัด)
  • 1 <= a, b, c <= 10^9

แนวทาง — ต้องใช้อะไร & คิดยังไง

ข้อนี้ใช้การพิจารณาทีละ bit เพราะในนิยามของ OR แต่ละ bit position เป็นอิสระต่อกัน ผลของ bit หนึ่งไม่กระทบอีก bit เราจึงตัดสินใจแยกทีละตำแหน่งได้เลย

แทนที่จะลองพลิกทุกแบบ (ซึ่งมีความเป็นไปได้มหาศาล) เราดู bit ตรงตำแหน่งเดียวกันของ a, b, c พร้อมกันด้วย & 1 แล้ว shift ทุกตัวไปขวาทีละตำแหน่งด้วย >> 1 จนกว่าทั้งสามจะกลายเป็น 0

  1. initialize flips = 0 แล้ว loop ไปเรื่อย ๆ ตราบใดที่ a หรือ b หรือ c ยังไม่เป็น 0
  2. ดึง bit ขวาสุดของทั้งสาม: bit_a = a & 1, bit_b = b & 1, bit_c = c & 1
  3. ถ้า bit_c เป็น 1: ต้องมี a หรือ b เป็น 1 สักตัว ถ้าทั้งคู่เป็น 0 ให้ flips += 1
  4. ถ้า bit_c เป็น 0: ทั้ง a และ b ห้ามมี 1 เลย ให้ flips += bit_a + bit_b
  5. shift ทั้งสามไปขวาหนึ่งตำแหน่ง (a >>= 1, b >>= 1, c >>= 1) แล้ว loop ต่อ สุดท้าย return flips
จุดพลาดที่พบบ่อย

กรณี bit_c เป็น 0 อย่านับแค่ 1 ครั้ง ต้องพลิกทุก bit 1 ที่มีอยู่ ถ้าทั้ง a และ b เป็น 1 คือต้องพลิก 2 ครั้ง จึงบวก bit_a + bit_b ไม่ใช่บวก 1

ไล่ทีละสเต็ป

จำลอง a = 2 (010), b = 6 (110), c = 5 (101) ไล่จาก bit ขวาสุด

รอบbit_abit_bbit_cการพลิกflips รวม
1001c=1 แต่ทั้งคู่ 0 → +11
2110c=0 → พลิก 1+13
3011c=1 มี b=1 แล้ว → +03
▶ เฉลยละเอียด (ลองเองก่อนนะ)
เฉลย (Python) — โค้ดนี้รันได้จริงpython
def min_flips(a, b, c):
    flips = 0
    # วนทีละบิตจนกว่าทั้งสามตัวจะหมดบิต
    while a or b or c:
        bit_a = a & 1
        bit_b = b & 1
        bit_c = c & 1
        if bit_c == 1:
            # c ต้องการ 1 ตรงตำแหน่งนี้
            # ถ้า a หรือ b มี 1 อยู่แล้วก็พอ ไม่งั้นต้องพลิก 1 ครั้ง
            if bit_a == 0 and bit_b == 0:
                flips += 1
        else:
            # c ต้องการ 0 ตรงตำแหน่งนี้ ต้องพลิกทุกบิตที่เป็น 1 ให้เป็น 0
            flips += bit_a + bit_b
        # เลื่อนไปดูบิตถัดไป
        a >>= 1
        b >>= 1
        c >>= 1
    return flips

print(min_flips(2, 6, 5))  # 3
print(min_flips(4, 2, 6))  # 0
print(min_flips(1, 2, 3))  # 0
Output
3
0
0

โจทย์นี้แยกคิดทีละ bit ได้ เพราะแต่ละ bit position เป็นอิสระต่อกันในนิยามของ OR เราดู bit ขวาสุดของ a, b, c พร้อมกันด้วย & 1 แล้ว shift ทุกตัวไปขวาทีละตำแหน่งด้วย >>= 1 จนกว่าทั้งสามจะกลายเป็น 0 (loop จบ)

เหตุผลของแต่ละกรณี ถ้า c ต้องการ bit 1 (bit_c == 1) แค่มี a หรือ b เป็น 1 สักตัวก็พอ เพราะ OR ให้ 1 ทันที ต้องพลิกก็ต่อเมื่อทั้งคู่เป็น 0 (พลิกตัวใดตัวหนึ่งเป็น 1 นับ 1 ครั้ง) แต่ถ้า c ต้องการ bit 0 ทั้ง a และ b ห้ามมี 1 เลย เพราะ OR จะให้ 1 ทันที ดังนั้นทุก bit 1 ต้องถูกพลิกทิ้ง จำนวนครั้งจึงเท่ากับ bit_a + bit_b

Time O(k) เมื่อ k คือจำนวน bit ของเลขที่มากที่สุด (ราว 32 bit สำหรับ int ทั่วไป จึงถือว่าคงที่) · Space O(1) ใช้ตัวแปรนับไม่กี่ตัว

💡 สรุป pattern

เมื่อโจทย์เกี่ยวกับ AND/OR/XOR ระหว่างหลายค่าและแต่ละ bit ไม่ขึ้นต่อกัน ให้ไล่พิจารณาทีละ bit ด้วย & 1 กับ >> 1 แล้วสรุปทีละตำแหน่ง — เปลี่ยนปัญหาใหญ่เป็นการตัดสินใจย่อยที่ง่ายมาก