ปัญหา: กำหนด Polygon = List of Points `[(x1,y1), (x2,y2), ..., (xn,yn)]` และ Point `pt = (x,y)` หาว่า pt อยู่ใน Polygon หรือบนขอบ (Boundary)
ใช้ Ray Casting Algorithm (Even-Odd Rule): ยิง Ray แนวนอนไปทางขวา นับจำนวนครั้งที่ตัด Edge ของ Polygon → คี่ = ใน, คู่ = นอก
อัลกอริทึม
- Loop ทุก Edge `(x0,y0)` → `(x1,y1)` ของ Polygon
- ถ้า `pt.y` ไม่อยู่ระหว่าง `y0` และ `y1` → Skip (Ray ไม่ตัด Edge นี้)
- ถ้า `pt.x < min(x0,x1)` → Skip (Ray ตัด Edge ก่อนถึง pt)
- คำนวณ `cur_x` = จุดตัดของ Ray กับ Edge
- Flip `ans` (XOR) ถ้า `pt.x > cur_x`
การติดตั้ง
class Solution:
def solve(self, polygon, pt):
ans = False
n = len(polygon)
for i in range(n):
x0, y0 = polygon[i]
x1, y1 = polygon[(i + 1) % n] # Wrap around
# Check if horizontal ray at pt.y intersects this edge
if not (min(y0, y1) < pt[1] <= max(y0, y1)):
continue
# Ray starts at pt.x, goes right
if pt[0] < min(x0, x1):
continue
# Intersection x-coordinate
if x0 == x1: # Vertical edge
cur_x = x0
else:
cur_x = x0 + (pt[1] - y0) * (x1 - x0) / (y1 - y0)
# Flip if intersection is to the right of point
ans ^= (pt[0] > cur_x)
return ans
# Test
ob = Solution()
points = [(0, 0), (1, 3), (4, 4), (6, 2), (4, 0)]
pt = (3, 1)
print(ob.solve(points, pt)) # True
คำอธิบาย
Loop Edge ทุกคู่ (i, i+1 mod n)
เงื่อนไข `min(y0,y1) < pt[1] <= max(y0,y1)`: Ray ตัด Edge ได้ก็ต่อเมื่อ pt.y อยู่ในช่วง Y ของ Edge (ใช้ < และ <= เพื่อจัดการ Vertex ให้ถูกต้อง)
`pt[0] < min(x0,x1)`: Ray เริ่มต้นซ้ายกว่า Edge → ไม่ตัด
`cur_x`: จุดตัด X ของ Ray (y=pt.y) กับ Line Segment
`ans ^= (pt[0] > cur_x)`: XOR Flip เมื่อ Ray ตัด Edge ขวาของ Point
Edge Cases การจัดการ
| Case | Handling |
|---|---|
| Point บน Vertex | เงื่อนไข `<= max` และ `< min` จัดการได้ (Count 1 ครั้ง) |
| Point บน Edge | `pt[0] == cur_x` → `False` → ไม่ Flip → คืน True (อยู่บนขอบ) |
| Horizontal Edge | `y0 == y1` → `min==max` → Condition Fail → Skip (ถูกต้อง) |
| Vertical Edge | `x0 == x1` → `cur_x = x0` (ตรง) |