On this page
ข้อ 69 · LC1318 Minimum Flips to Make a OR b Equal to c (พลิกบิตน้อยสุด) 🟡
หาจำนวน 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
- Input:
- a = 2 (0b010), b = 6 (0b110), c = 5 (0b101)
- Output:
- 3
- Explanation:
- ต้องพลิกจน a=1 (0b001), b=4 (0b100) จึงจะได้ a OR b = 5 พอดี
- Input:
- a = 4, b = 2, c = 6
- Output:
- 0
- Explanation:
- 4 OR 2 = 6 อยู่แล้ว ไม่ต้องพลิกเลย
- Input:
- a = 1, b = 2, c = 3
- Output:
- 0
- Explanation:
- 1 OR 2 = 3 อยู่แล้ว ไม่ต้องพลิกเลย
- Input:
- a = 4, b = 2, c = 7
- Output:
- 1
- Explanation:
- 4 OR 2 = 6 (0b110) ขาด bit ขวาสุด พลิก a หรือ b ให้ bit ขวาสุดเป็น 1 แค่ครั้งเดียวก็ได้ 7 (0b111)
- 1 <= a, b, c <= 10^9
แนวทาง — ต้องใช้อะไร & คิดยังไง
ข้อนี้ใช้การพิจารณาทีละ bit เพราะในนิยามของ OR แต่ละ bit position เป็นอิสระต่อกัน ผลของ bit หนึ่งไม่กระทบอีก bit เราจึงตัดสินใจแยกทีละตำแหน่งได้เลย
แทนที่จะลองพลิกทุกแบบ (ซึ่งมีความเป็นไปได้มหาศาล) เราดู bit ตรงตำแหน่งเดียวกันของ a, b, c พร้อมกันด้วย & 1 แล้ว shift ทุกตัวไปขวาทีละตำแหน่งด้วย >> 1 จนกว่าทั้งสามจะกลายเป็น 0
- initialize flips = 0 แล้ว loop ไปเรื่อย ๆ ตราบใดที่ a หรือ b หรือ c ยังไม่เป็น 0
- ดึง bit ขวาสุดของทั้งสาม: bit_a = a & 1, bit_b = b & 1, bit_c = c & 1
- ถ้า bit_c เป็น 1: ต้องมี a หรือ b เป็น 1 สักตัว ถ้าทั้งคู่เป็น 0 ให้ flips += 1
- ถ้า bit_c เป็น 0: ทั้ง a และ b ห้ามมี 1 เลย ให้ flips += bit_a + bit_b
- 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_a | bit_b | bit_c | การพลิก | flips รวม |
|---|---|---|---|---|---|
| 1 | 0 | 0 | 1 | c=1 แต่ทั้งคู่ 0 → +1 | 1 |
| 2 | 1 | 1 | 0 | c=0 → พลิก 1+1 | 3 |
| 3 | 0 | 1 | 1 | c=1 มี b=1 แล้ว → +0 | 3 |
▶ เฉลยละเอียด (ลองเองก่อนนะ)
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)) # 03
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) ใช้ตัวแปรนับไม่กี่ตัว
เมื่อโจทย์เกี่ยวกับ AND/OR/XOR ระหว่างหลายค่าและแต่ละ bit ไม่ขึ้นต่อกัน ให้ไล่พิจารณาทีละ bit ด้วย & 1 กับ >> 1 แล้วสรุปทีละตำแหน่ง — เปลี่ยนปัญหาใหญ่เป็นการตัดสินใจย่อยที่ง่ายมาก