BJO 2352 반도체 설계
n개의 포트를 다른 n개의 포트와 연결해야 할 때가 있다. 연결선이 꼬이지 않게 최대 몇개를 연결 할 수 있는지 프로그램 작성
입력값 : 첫째 줄에 최대 연결 개수 다음 줄에 차례로 1번 포트와 연결이 되어야 하는 포트번호~ 쭉 등장.
단순히 떠오르는 생각
4 2 6 3 1 5 예제를 보면 4는 4,6 2개 2는 2,3,5 3개 6은 4,6 2개 3은 2,3,5 3개 1은 1,5 2개 5는 2,3,5 3개.
즉 자신을 포함한 자기보다 큰 수가 오른쪽에 몇개 있나 세보면 되지 않을까?
코드
import sys
T = int(sys.stdin.readline())
arr = list(map(int, sys.stdin.readline().split()))
max = 0
for i in range(T):
count = 1
for j in range(1, T-i):
if arr[i] < arr[j]:
count += 1
if(max < count):
max = count
print(max)
-> 시간초과.
LIS
질문 검색에 시간초과를 클릭해보니, 이 블로그를 참조하래서, 이 블로그를 참조한다 이런 문제를 흔히 LIS라고 하나보다. BinarySearch + dp를 이용하여 풀어야 한다고 한다.
[LIS(Longest Increasing Subsequence)]https://dyngina.tistory.com/16 LIS: 최대 부분 증가 수열이다. 단순히 예를 들어 10, 20, 40, 30, 70, 50, 60 이라는 수열이 있을 대, 10 20 30 50 60 . 즉 앞에서 뒤로 숫자를 선택해 나갈 때, 증가하는 순서대로 고를 때, 최대 길이를 갖는 수열이라고 할 수 있다. 여기서는 길이가 5인 LIS를 갖겠다.
유일한 예제는 아니다. 10 20 40 50 60 도 답이 되기 때문이다.
N^2는 누구나 코딩 가능할 정도로 단순하다.
방법은 두 가지가 있다. 간단하지만 역추적이 불가능한 LowerBound를 이용한 방법이 있고, 역추적이 가능한 Index Tree 방법이 있다.
(LowerBound =이진 탐색 기반의 탐색방법)을 이용하겠다.
리스트를 하나 생성한 뒤, LNF(나올 수 없는 가장 작은 값)을 삽입해준다. 이후 매번 수열을 볼 때마다, 리스트의 맨 뒤 원소와 현재 보고있는 수열의 원소를 비교하여, 수열의 원소가 더 클시 리스트에 push 해주고, LIS를 1 증가시켜준다.
수열의 원소가 리스트의 맨 뒤 원소보다 작을 경우, lower_bound를 이용하여, 최적의 자리를 찾은 뒤. 그 자리의 값을 해당 수열의 원소로 교체해버린다.
파이썬 bisect 알고리즘
직접 반복문을 만들어 이진 탐색 알고리즘을 구현하면 다음과 같다.
def bisect(a, x, lo=0, hi=None):
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if a[mid] < x:
lo = mid + 1
else:
hi = mid
return lo
mylist = [1, 2, 3, 7, 9, 11, 33]
print(bisect(mylist, 3))
여기서 python의 bisect.bisect method를 사용하면 이 코드를 간략하게 만들 수 있다.
import bisect
mylist = [1, 2, 3, 7, 9, 11, 33]
print(bisect.bisect(mylist, 3))
풀이코드
- 맨 처음 값부터 하나하나 입력 받는다
- 입력받은 값이 벡터 안에 있는 값들 보다 크면 벡터의 맨 뒤에 넣는다.
- 벡터의 처음부터 검색했을 때, 입력 받은 값보다 큰 값이 있다면, 그 값과 교체한다.
- 벡터의 원소 갯수를 출력한다.
코드
import sys
import bisect
DP = []
def LIS(start, end, target):
while(start < end):
mid = (start+end)//2
if DP[mid] > target:
end = mid
elif DP[mid] < target:
start = mid + 1
else:
return -1
return end
if __name__ == "__main__":
T = int(sys.stdin.readline())
arr = list(map(int, sys.stdin.readline().split()))
DP.append(arr[0])
size = 0
for i in range(1, T):
if arr[i] > DP[size]:
size += 1
DP.append(arr[i])
# DP[size] = arr[i]
else:
idx = LIS(0, size, arr[i])
if(idx != -1):
DP[idx] = arr[i]
print(size+1)
참고자료 [참고블로그]https://blog.encrypted.gg/452 [LIS 참고 블로그]https://jason9319.tistory.com/113 [파이썬 이진탐색]https://programmers.co.kr/learn/courses/4008/lessons/13173 [이진탐색 알고리즘]https://debuglog.tistory.com/69 [최적화된 LIS]https://jins-dev.tistory.com/entry/%EC%B5%9C%EC%A0%81%ED%99%94%EB%90%9C-LISLongest-Increasing-Subsequence-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EA%B3%BC-%ED%95%B4-%EC%B0%BE%EA%B8%B0 [bisect 사용법]https://m.blog.naver.com/PostView.nhn?blogId=wideeyed&logNo=221389084876&proxyReferer=https%3A%2F%2Fwww.google.com%2F [마지막 참고 블로그]https://m.blog.naver.com/dmsrb1002/221413242771