Skip to content

Commit 43a5d48

Browse files
committed
solved(python): baekjoon 2162
1 parent 80c4489 commit 43a5d48

4 files changed

Lines changed: 123 additions & 0 deletions

File tree

baekjoon/python/2162/__init__.py

Whitespace-only changes.

baekjoon/python/2162/main.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import sys
2+
from collections import Counter
3+
4+
read = lambda: sys.stdin.readline().rstrip()
5+
6+
7+
class Problem:
8+
def __init__(self):
9+
self.n = int(read())
10+
self.lines = []
11+
self.root = list(range(self.n))
12+
13+
for idx in range(self.n):
14+
x1, y1, x2, y2 = tuple(map(int, read().split()))
15+
self.lines.append(((x1, y1), (x2, y2)))
16+
17+
def solve(self) -> None:
18+
for line_x in range(self.n - 1):
19+
for line_y in range(line_x + 1, self.n):
20+
(a, b), (c, d) = self.lines[line_x], self.lines[line_y]
21+
if self.is_cross(a, b, c, d):
22+
self.union(line_x, line_y)
23+
24+
group = [self.find(num) for num in range(self.n)]
25+
print(len(set(group)))
26+
print(max(Counter(group).values()))
27+
28+
def union(self, x: int, y: int):
29+
root_x, root_y = self.find(x), self.find(y)
30+
if root_x != root_y:
31+
self.root[root_y] = root_x
32+
33+
def find(self, num: int) -> int:
34+
if num != self.root[num]:
35+
self.root[num] = self.find(self.root[num])
36+
return self.root[num]
37+
38+
def is_cross(self, a: tuple[int, int], b: tuple[int, int], c: tuple[int, int], d: tuple[int, int]) -> bool:
39+
ccw_ab, ccw_cd = self.ccw((a, b, c)) * self.ccw((a, b, d)), self.ccw((c, d, a)) * self.ccw((c, d, b))
40+
41+
if ccw_ab == 0 and ccw_cd == 0:
42+
return self.is_overlap(a, b, c, d)
43+
44+
return ccw_ab <= 0 and ccw_cd <= 0
45+
46+
def ccw(self, points: tuple[tuple[int, int], tuple[int, int], tuple[int, int]]) -> int:
47+
(x1, y1), (x2, y2), (x3, y3) = points
48+
result = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)
49+
return 0 if result == 0 else (result // abs(result))
50+
51+
def is_overlap(self, a: tuple[int, int], b: tuple[int, int], c: tuple[int, int], d: tuple[int, int]) -> bool:
52+
return (
53+
min(a[0], b[0]) <= max(c[0], d[0])
54+
and min(c[0], d[0]) <= max(a[0], b[0])
55+
and min(a[1], b[1]) <= max(c[1], d[1])
56+
and min(c[1], d[1]) <= max(a[1], b[1])
57+
)
58+
59+
60+
if __name__ == "__main__":
61+
Problem().solve()

baekjoon/python/2162/sample.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[
2+
{
3+
"input": [
4+
"3",
5+
"1 1 2 3",
6+
"2 1 0 0",
7+
"1 0 1 1"
8+
],
9+
"expected": [
10+
"1",
11+
"3"
12+
]
13+
},
14+
{
15+
"input": [
16+
"3",
17+
"-1 -1 1 1",
18+
"-2 -2 2 2",
19+
"0 1 -1 0"
20+
],
21+
"expected": [
22+
"2",
23+
"2"
24+
]
25+
}
26+
]

baekjoon/python/2162/test_main.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import json
2+
import os.path
3+
import unittest
4+
from io import StringIO
5+
from unittest.mock import patch
6+
7+
from parameterized import parameterized
8+
9+
from main import Problem
10+
11+
12+
def load_sample(filename: str):
13+
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename)
14+
15+
with open(path, "r") as file:
16+
return [(case["input"], case["expected"]) for case in json.load(file)]
17+
18+
19+
class TestCase(unittest.TestCase):
20+
@parameterized.expand(load_sample("sample.json"))
21+
def test_case(self, case: str, expected: list[str]):
22+
# When
23+
with (
24+
patch("sys.stdin.readline", side_effect=case),
25+
patch("sys.stdout", new_callable=StringIO) as output,
26+
):
27+
Problem().solve()
28+
29+
result = output.getvalue().rstrip()
30+
31+
# Then
32+
self.assertEqual("\n".join(expected), result)
33+
34+
35+
if __name__ == "__main__":
36+
unittest.main()

0 commit comments

Comments
 (0)