-
Notifications
You must be signed in to change notification settings - Fork 1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
""" | ||
풀이시간: 10분 | ||
|
||
<제한사항> | ||
- 각 던전은 하루에 한 번만 탐험이 가능하다 -> 하루 동안 최대한 많이 탐험하려고 한다. | ||
- dungeons의 길이(즉, 던전의 개수)는 1 이상 8 이하이다. | ||
|
||
|
||
<solution> | ||
- dungeons의 길이가 최대 8이기 때문에, 모든 경우의 수를 확인하는 브루트 포스(완전탐색)으로 문제 풀이가 가능하다. | ||
- 얼핏보면 그리디로 접근할 수 있나? 생각할 수 있지만, n(dungeons의 길이)이 매우 작고 몇 가지 그리디를 생각해봐도 반례가 무수히 많이 존재하기 때문에 그리디로 풀 수 없다. | ||
- 1) 매번 가장 적은 소모 피로도를 가진 던전부터 탐험 -> k = 80, dungeons=[[30, 10], [80, 20], [60, 30]] -> 1개(정답은 3개) | ||
- 2) 매번 가장 큰 최소 필요 피로도를 가진 던전부터 탐험 -> 문제의 예시와 동일 | ||
기타 등등 | ||
|
||
<시간복잡도> | ||
dungeons의 길이는 최대 8로 연산횟수는 최대 8! = 40320으로 시간 내에 충분히 통과가 가능하다. | ||
|
||
dungeons의 모든 순열(permutations)을 만들면 최대 8! = 40320이며 | ||
각 순열마다, 던전들을 반복하며 now와 cnt 값을 계산하기 때문에 모든 순열에 대해 O(n)이 걸린다. | ||
|
||
따라서, 전체적으로 솔루션의 시간 복잡도는 O(n * n!)로 최대 8 * 8! = 322560의 연산횟수를 갖지만 충분히 시간 내에 통과 가능하다. | ||
|
||
|
||
<번외> | ||
이 문제는 어떤 던전부터 방문할 것인지 '순서'가 중요하고 순열을 임의의 수열을 다른 '순서'로 섞는 연산이기 때문에 순열로 풀이가 가능하다. | ||
마찬가지로 permuations 라이브러리를 사용하지 않고, next_permutation을 구현해서 풀 수도 재귀를 이용해 풀 수도 있다. | ||
""" | ||
|
||
from itertools import permutations | ||
from typing import List | ||
|
||
def solution(k:int, dungeons:List) -> int: | ||
answer: int = -1 # 유저가 탐험할 수 있는 최대 던전 수 | ||
for permu in permutations(dungeons): | ||
now = k # 현재 피로도 초기화 | ||
cnt = 0 # 유저가 탐험할 수 있는 던전의 수 초기화 | ||
for need, use in permu: # 모든 경우의 수 | ||
if now >= need: # 현재 피로도 >= 최소 필요 피로도로 해당 던전 탐험 가능 | ||
now -= use # 해당 던전을 탐험 -> 현재 피로도 - 소모 피로도 | ||
cnt += 1 # 탐험한 던전 수 갱신 | ||
if cnt > answer: # 탐험한 던전 수가 지금까지 탐험할 수 있는 최대 던전 수보다 클 때만 갱신 | ||
answer = cnt | ||
|
||
return answer | ||
|
||
|
||
""" | ||
테스트 1 〉 통과 (0.01ms, 10.5MB) | ||
테스트 2 〉 통과 (0.01ms, 10.5MB) | ||
테스트 3 〉 통과 (0.04ms, 10.5MB) | ||
테스트 4 〉 통과 (0.13ms, 10.5MB) | ||
테스트 5 〉 통과 (0.39ms, 10.5MB) | ||
테스트 6 〉 통과 (4.94ms, 10.7MB) | ||
테스트 7 〉 통과 (26.58ms, 10.6MB) | ||
테스트 8 〉 통과 (26.19ms, 10.6MB) | ||
테스트 9 〉 통과 (0.09ms, 10.6MB) | ||
테스트 10 〉통과 (3.18ms, 10.7MB) | ||
테스트 11 〉통과 (0.02ms, 10.5MB) | ||
테스트 12 〉통과 (25.53ms, 10.5MB) | ||
테스트 13 〉통과 (19.08ms, 10.6MB) | ||
테스트 14 〉통과 (19.43ms, 10.7MB) | ||
테스트 15 〉통과 (16.58ms, 10.6MB) | ||
테스트 16 〉통과 (3.44ms, 10.5MB) | ||
테스트 17 〉통과 (30.41ms, 10.6MB) | ||
테스트 18 〉통과 (0.01ms, 10.6MB) | ||
테스트 19 〉통과 (0.09ms, 10.7MB) | ||
""" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
""" | ||
다음 순열(next_permutation)을 이용한 풀이 | ||
- 풀이시간: 20분 | ||
- 시간복잡도: O(N! * N * N) -> 최대 2,580,480번 연산으로 시간 내에 통과가능 | ||
1. 전체 순열을 구하는 부분: O(N! * N) | ||
2. 각 순열에 대해 던전을 탐험하는 부분: O(N) | ||
""" | ||
from typing import List | ||
|
||
# 다음 순열로 풀기 | ||
def next_permutation(a): # 순열 a를 계속 변경 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. next permutation 알고리즘에 대해서 처음으로 알아갑니다! 이번 문제와 같이 특정 개수의 순열을 뽑아낼 필요가 없는 문제에 대해서는 재귀로 구현한 순열보다 훨씬 효율적으로 해결할 수 있는 알고리즘인 것 같아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 Next Permutation이라는 알고리즘을 처음 들어 개념을 다시 보고 왔습니다. 처음보는 개념이라 완전한 이해는 아니지만 이런 방법이 있다는 걸 깨달을 수 있어 좋았습니다! 감사합니다 수민님! 👍 |
||
i = len(a) - 1 # 뒤에서부터 | ||
# 1. a[i-1] < a[i]를 만족하는 가장 큰 i를 찾는다 -> 가장 긴 감소수열 | ||
while i > 0 and a[i-1] >= a[i]: # 마지막 수열 | ||
i -= 1 | ||
if i <= 0: # 0번째 index까지 확인한 경우(내림차순) | ||
return False # 다음 순열이 없음(마지막 순열) | ||
j = len(a)-1 | ||
# 2. j >= i 이면서 a[j] > [i-1]를 만족하는 가장 큰 j를 찾는다 | ||
while a[j] <= a[i-1]: | ||
j -= 1 | ||
# 3. a[i-1]과 a[j]를 swap | ||
a[i-1], a[j] = a[j], a[i-1] | ||
|
||
j = len(a)-1 | ||
# 4. A[i]부터 순열을 뒤집는다. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 알고리즘을 보고 매번 정렬을 거치는 과정이 오히려 연산량 측면에서 부담이지 않을까 싶었는데, 이미 내림차순 정렬되어있기 때문에 대칭되는 부분만 바꿔주면 되어서 연산량 측면에서 괜찮은 것이라고 이해해도 될까요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵! 이 부분은 O(N)이 걸려요!! 매번 정렬 알고리즘으로 정렬한다면 O(NlogN)이 걸리겠지만..! 예를 들어, 다음과 같은 순열이 있을 때 순열: 7 2 4 6 5 3 1 그래서 결과적으로 수열의 크기가 N이라고 할 때,
|
||
while i < j: | ||
a[i], a[j] = a[j], a[i] | ||
i += 1 | ||
j -= 1 | ||
|
||
return True # 다음 순열이 있음 | ||
|
||
|
||
def solution(k:int, dungeons:List) -> int: | ||
answer: int = -1 # 유저가 탐험할 수 있는 최대 던전 수 | ||
n = len(dungeons) | ||
a = list(range(n)) | ||
while True: # 모든 순열 구하기 | ||
cnt = 0 | ||
now = k | ||
for i in a: | ||
need, use = dungeons[i] | ||
if now >= need: | ||
cnt += 1 | ||
now -= use | ||
if cnt > answer: | ||
answer = cnt | ||
if not next_permutation(a): # 다음 순열이 없다면 종료 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 캬 자연스럽게 실행되는 로직까지 이쁜 코드 같아요! |
||
break | ||
return answer | ||
|
||
""" | ||
테스트 1 〉 통과 (0.03ms, 10.2MB) | ||
테스트 2 〉 통과 (0.06ms, 10.3MB) | ||
테스트 3 〉 통과 (0.14ms, 10.2MB) | ||
테스트 4 〉 통과 (0.16ms, 10.2MB) | ||
테스트 5 〉 통과 (0.91ms, 10.4MB) | ||
테스트 6 〉 통과 (6.38ms, 10.3MB) | ||
테스트 7 〉 통과 (58.50ms, 10.2MB) | ||
테스트 8 〉 통과 (57.08ms, 10.2MB) | ||
테스트 9 〉 통과 (0.14ms, 10.3MB) | ||
테스트 10 〉통과 (6.22ms, 10.4MB) | ||
테스트 11 〉통과 (0.03ms, 10.3MB) | ||
테스트 12 〉통과 (51.09ms, 10.3MB) | ||
테스트 13 〉통과 (63.14ms, 10.3MB) | ||
테스트 14 〉통과 (47.68ms, 10.3MB) | ||
테스트 15 〉통과 (60.85ms, 10.4MB) | ||
테스트 16 〉통과 (5.84ms, 10.4MB) | ||
테스트 17 〉통과 (50.85ms, 10.2MB) | ||
테스트 18 〉통과 (0.03ms, 10.4MB) | ||
테스트 19 〉통과 (0.26ms, 10.3MB) | ||
""" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
""" | ||
# 백트래킹(재귀)을 이용한 풀이 | ||
- 풀이시간: 15분 | ||
- 시간복잡도: O(2 ** N) -> 최대 256번 연산으로 시간 내에 통과가능 | ||
- 던전의 개수를 n이라고 하면, 각 던전에 대해서 선택/비선택의 2가지 경우가 있기 때문. | ||
""" | ||
from typing import List | ||
|
||
answer = 0 | ||
n = 0 | ||
visited = [] | ||
|
||
|
||
def go(k:int, cnt:int, dungeons:List) -> None: | ||
global answer | ||
if cnt > answer: # 방문할 수 있는 던전의 개수가 이전까지보다 더 많다면 갱신 | ||
answer = cnt | ||
|
||
for i in range(n): | ||
if k >= dungeons[i][0] and not visited[i]: | ||
visited[i] = True # 방문처리 | ||
go(k - dungeons[i][1], cnt+1, dungeons) # 현재 피로도 - 소모 피로도 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 와 백트래킹으로 풀이를 할 수 있다는게 정말 놀랍네요 공부가 더욱 필요하다는 것을 절실히 느끼게 되었습니다. 감사합니다 수민님! 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 완탐은 결국 재귀로도 풀 수 있어야 하는 거 같기는해요.. ㅎㅎ |
||
visited[i] = False # 백트래킹 | ||
|
||
def solution(k:int, dungeons:List) -> int: | ||
global n, visited | ||
n = len(dungeons) # 던전의 개수 | ||
visited = [False] * n # 던전 방문처리 배열 | ||
go(k, 0, dungeons) | ||
return answer | ||
|
||
|
||
""" | ||
테스트 1 〉 통과 (0.03ms, 10.1MB) | ||
테스트 2 〉 통과 (0.03ms, 10.2MB) | ||
테스트 3 〉 통과 (0.02ms, 10.2MB) | ||
테스트 4 〉 통과 (0.28ms, 10.3MB) | ||
테스트 5 〉 통과 (1.08ms, 10.2MB) | ||
테스트 6 〉 통과 (2.82ms, 10.2MB) | ||
테스트 7 〉 통과 (17.61ms, 10.1MB) | ||
테스트 8 〉 통과 (47.09ms, 10.2MB) | ||
테스트 9 〉 통과 (0.02ms, 10.2MB) | ||
테스트 10 〉통과 (0.99ms, 10.3MB) | ||
테스트 11 〉통과 (0.01ms, 10.2MB) | ||
테스트 12 〉통과 (2.13ms, 10.3MB) | ||
테스트 13 〉통과 (0.26ms, 10.2MB) | ||
테스트 14 〉통과 (0.09ms, 10.2MB) | ||
테스트 15 〉통과 (0.04ms, 10.2MB) | ||
테스트 16 〉통과 (0.04ms, 10.2MB) | ||
테스트 17 〉통과 (0.10ms, 10.2MB) | ||
테스트 18 〉통과 (0.02ms, 10.2MB) | ||
테스트 19 〉통과 (0.08ms, 10.3MB) | ||
""" | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정확히 방식이 동일해서 보기 편했습니다.