1
2
3
4
5
6
7
8
import sys
import numpy as np
import random, math
import logging, argparse, yaml, copy
import matplotlib.pyplot as plt
sys.path.append('/home/swyoo/algorithm')
from utils.verbose import logging_time, printProgressBar
from copy import deepcopy
1767. 프로세서 연결하기
c++ 로 구현된 좋은 코드를 찾아서 이를 바탕으로 python으로 구현하였다.
Notation은 $n \times n$ array 가 주어졌을때,
이 안에 들어있는 processor의 갯수를 $M$ 이라 하자.
각 processor 마다 동,서,남,북,행동X 5가지 cases을 모두 고려해 전선을 까는 경우에 대해 모든 프로세서를 탐색 해보고,
프로세서를 가장 많이 가동 할 수 있으면서 전선이 최소로 필요한 상황에서 최소 전선의 수를 찾는 것이 목표이다.
최악의 경우 모든 cases를 enumerate하는 경우 $O(n5^M)$ 시간이 걸린다.
- 각 프로세서 마다 최대 5번의 방향성을 고려해야 함.
inline, drawline
함수는 $O(n)$ 시간이 걸림.가정상황: $n^2 < 5^M$
하지만, 밑의 방식으로 코딩하면 drawline을 통해 주변 프로세서들이 가능한 방향이 pruning되기 때문에
최악의 경우와 같이 모두 조사하는 상황은 거의 발생하지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
pnum = 0 # 찾아야할 파워가 연결된 프로세서 갯수
ans = 1e8 # 찾아야할 사용된 전선들의 최소길이
n = 0
a = []
def isline(r, c, dir):
global a, n
if dir == 0:
for j in range(c + 1, n):
if a[r][j] != 0:
return False
elif dir == 1:
for j in range(c - 1, -1, -1):
if a[r][j] != 0:
return False
elif dir == 2:
for i in range(r + 1, n):
if a[i][c] != 0:
return False
elif dir == 3:
for i in range(r - 1, -1, -1):
if a[i][c] != 0:
return False
return True
def drawline(r, c, dir, fill):
""" fill=2 means draw
fill=0 means delete. """
global a, n
line = 0
if dir == 0:
for j in range(c + 1, n):
a[r][j] = fill
line += 1
elif dir == 1:
for j in range(c - 1, -1, -1):
a[r][j] = fill
line += 1
elif dir == 2:
for i in range(r + 1, n):
a[i][c] = fill
line += 1
elif dir == 3:
for i in range(r - 1, -1, -1):
a[i][c] = fill
line += 1
return line
@logging_time
def solve():
global a
def dfs(p, pidx, nump, line):
"""
p 는 프로세서 인덱싱 정보, pidx는 조사할 프로세서 인덱스,
nump는 power가 연결된 프로세서 최대 수, line은 조사중인 전선 길이
"""
global a, pnum, ans, n
if pidx == len(p): # 모든 프로세서에 대해 조사 끝났을 경우
if pnum < nump: # 더 많은 프로세서를 가동할 경우를 찾을 경우
ans = line
pnum = nump
elif pnum == nump: # 프로세서는 이전과 일치하지만,
if ans > line: # 더 적은 전선이 필요한 상황을 찾을 경우
ans = line
return
for i in range(4): # 동, 서, 남, 북 에 대해 조사
if isline(*p[pidx], i): # 현재 프로세서를 기준으로 i 방향으로 전선을 까는 것이 가능한지 조사
# 전선을 drawline을 통해 깔고, 현재 line수를 업데이트한 상태로 다음 process에 대해 조사
dfs(p, pidx + 1, nump + 1, line + drawline(*p[pidx], dir=i, fill=2))
drawline(*p[pidx], dir=i, fill=0) # roll back
dfs(p, pidx + 1, nump, line) # no drawing case
# dfs 시작지점 지정을 위해 연결되지 않은 프로세서 인덱싱(가장자리는 이미 연결됨)
p = []
for i in range(1, n - 1):
for j in range(1, n - 1):
if a[i][j] == 1:
p.append((i, j))
# 가장 자리를 제외하고, 전선을 연결해가며 프로세서 연결이 최대가 될때의 전선 길이 반환
dfs(p, pidx=0, nump=0, line=0)
1
2
3
4
5
6
7
8
9
10
11
12
13
if __name__ == "__main__":
sys.stdin = open('data/processor.txt')
T = int(sys.stdin.readline())
for tc in range(1, T+1):
n = int(sys.stdin.readline())
a = []
pnum = 0
ans = 1e8
for _ in range(n):
a.append(list(map(int, sys.stdin.readline().split())))
solve(verbose=True)
print("#{} {}".format(tc, ans))
1
2
3
4
5
6
7
WorkingTime[solve]: 0.89335 ms
#1 12
WorkingTime[solve]: 9.07135 ms
#2 10
WorkingTime[solve]: 56.23579 ms
#3 24
Review
복습하고자 다시 풀어보았다. 두 가지 주의 사항이 있었다.
- 문제의 조건
이 문제에서 분명히 확인해야하는 점은 선은 일직선으로만 그을 수 있고,
중간에 어떠한 프로세서나 줄 같은 방해물이 없어야 그 프로세서가 연결 될 수 있다. (실수로, 프로세서 끼리 연결되거나 줄끼리 겹치는 경우도 연결되도록 해버려서 한참 헤매었다.)
다음의 함수들이 조건을 만족시키기 위한 핵심 함수들이다.1 2
def go(i, j, ax0, ax1): # TODO ...
1 2
def adj(i, j): # TODO ...
1 2
def marking(i, j, draw, remove=False): # TODO ...
- pruning
pruning 하지않으면 시간초과로 통과 할 수 없다. 따라서, 한 프로세서가 power-on 되지 않는 경우에는
아무 선도 연결 하지 않은 case로 몰아서 처리하면, pruning 된다.1 2 3 4 5
def dfs(...): ... for isConnect, draw in adj(*pcs[pidx]): if isConnect: # pruning: it is important. ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import sys
from sys import stdin
import numpy as np
from utils.verbose import logging_time
sys.setrecursionlimit(10000)
@logging_time
def solution(grid):
ncore, ans, n = 0, 1e10, len(grid)
pcs = [(i, j) for i in range(1, n - 1) for j in range(1, n - 1) if grid[i][j]]
up, down, left, right = (-1, 0), (1, 0), (0, -1), (0, 1)
def go(i, j, ax0, ax1):
x, y = i + ax0, j + ax1
draw = []
while 0 <= x < n and 0 <= y < n:
if grid[x][y] != 0: return False, []
draw.append((x, y))
x, y = x + ax0, y + ax1
return True, draw
def adj(i, j):
for direction in up, down, left, right:
yield go(i, j, *direction)
def marking(i, j, draw, remove=False):
for x, y in draw:
grid[x][y] = 2 if not remove else 0
def dfs(pidx, core, line):
nonlocal ncore, ans
if pidx == len(pcs):
if ncore < core:
ncore, ans = core, line
elif ncore == core and ans > line:
ans = line
return
for isConnect, draw in adj(*pcs[pidx]):
if isConnect: # pruning: it is important.
marking(*pcs[pidx], draw=draw)
dfs(pidx + 1, core + 1, line=line + len(draw))
marking(*pcs[pidx], draw=draw, remove=True)
dfs(pidx + 1, core, line=line) # nothing case.
dfs(0, core=0, line=0)
return ans
stdin = open('data/processor.txt')
input = stdin.readline
T = int(input())
for t in range(1, T + 1):
n = int(input())
grid = [list(map(int, input().split())) for _ in range(n)]
ans = solution(grid, verbose=True)
print('#{} {}'.format(t, ans))
1
2
3
4
5
6
7
WorkingTime[solution]: 0.76890 ms
#1 12
WorkingTime[solution]: 8.89230 ms
#2 10
WorkingTime[solution]: 59.32403 ms
#3 24
Test
1
2
3
4
5
6
7
8
9
10
11
12
n = 10
grid = np.zeros(shape=(n, n), dtype=int).tolist()
pcs = [(random.randint(0, n - 1), random.randint(0, n - 1)) for _ in range(n)]
for x, y in pcs:
grid[x][y] = 1
print(np.array(grid))
print(solution(deepcopy(grid), verbose=True))
a = deepcopy(grid)
pnum, ans = 0, 1e8
solve(verbose=True)
ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 1 0 0 0]
[1 0 0 0 0 1 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 1 0 0 0]
[0 0 1 0 0 1 0 0 0 0]
[0 0 0 0 0 1 0 0 0 0]
[0 1 1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 1 0 0]]
WorkingTime[solution]: 20.28561 ms
18
WorkingTime[solve]: 19.77015 ms
1
18
Generate Toy Examples.
1
2
gridstr = '\n'.join(' '.join(str(e) for e in row) for row in np.array(grid).tolist())
print(gridstr)
1
2
3
4
5
6
7
8
9
10
11
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
1 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 1 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0 0 0
0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
1
2
grid = [list(map(int, rows.split())) for rows in gridstr.split('\n')]
grid
1
2
3
4
5
6
7
8
9
10
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0]]
1
print(solution(deepcopy(grid), verbose=True))
1
2
3
WorkingTime[solution]: 20.10751 ms
18
Leave a comment