# 난이도 <중>
1.문자열 압축
https://school.programmers.co.kr/learn/courses/30/lessons/60057
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
# 정답 풀이
#들여쓰기에 주의하세요
def get_shortened_length(target, unit_length):
length = len(target)
new_length = length
prev_substr = "" # 기준 문자열
count = 1 # 반복 횟수
# 부분 문자열 인덱스 [left, right)
left = 0
right = unit_length
# 오른쪽 인덱스가 길이 초과하기 전까지
while right <= length:
# 새로운 부분 문자열
curr_substr = target[left:right]
if prev_substr == curr_substr: # 반복되는 경우, 횟수 증가
count += 1
else: # 반복되지 않는 경우
# 압축한 경우
if count > 1:
new_length += len(str(count)) # 숫자 길이 만큼 문자열 증가
new_length -= (count - 1) * unit_length # 반복 횟수에 따른 문자열 단축
# 부분 문자열 교체, 횟수 초기화
prev_substr = curr_substr
count = 1
#else-end
# 부분 문자열 인덱스 증가
left += unit_length
right += unit_length
#while-end
# 남아있는 압축 문자열 반영하기
if count > 1:
new_length += len(str(count))
new_length -= (count - 1) * unit_length
return new_length
def solution(given_str):
answer = len(given_str)
for k in range(1, len(given_str) // 2 + 1):
# k: 단위 길이
answer = min(answer, get_shortened_length(given_str, k))
return answer
- 일단 문자열이 반복되는 지 검사할 때 1부터 문자열의 길이/2 +1 만큼 검사해야 한다. 반복문으로 검사함.
- 반복문으로 검사할 문자열과 반복 숫자(k)를 넣어서 검사 후 최솟값을 answer에 담는다.
- get_shortened_length() 함수를 따로 만들었다. 먼저 입력받은 문자열의 길이를 unit_length에따로 저장한다.(인덱스가 문자열을 넘어가는지 확인하기 위함) 바뀐 문자열의 길이를 저장할 new_length, 비교할 문자열 prev_substr, 반복 횟수를 저장할 count 선언.
- 문자열 슬라이싱으로 앞과 뒤의 문자열이 반복되는 지, 즉 같은지 검사할 것이다. 오른쪽 인덱스가 문자열 길이를 초과하기 전까지 prev_substr=문자열[left:right], curr_substr=문자열[left+unit_length:right+unit_length]로 저장 후 반복해서 비교한다.
- 부분 문자열을 계속 옮겨가면서 비교하므로 left와 right 인덱스는 unit_length만큼 계속 증가시킨다.
- 만약 반복되는 경우 count를 증가한다. 반복되지 않는 경우 count가 2이상이면(반복이 존재했었음) new_length에 압축한 문자열을 저장한다.
- 이것을 len(문자열)//2 + 1 만큼 반복하면 끝! return값을 answer와 계속 비교하며 최종적으로 최솟값이 남게한다.
2. 강의실 배정
https://www.acmicpc.net/problem/11000
11000번: 강의실 배정
첫 번째 줄에 N이 주어진다. (1 ≤ N ≤ 200,000) 이후 N개의 줄에 Si, Ti가 주어진다. (0 ≤ Si < Ti ≤ 109)
www.acmicpc.net
# 정답 풀이
import sys
import heapq as hq
input = sys.stdin.readline
# 필요한 강의실 수를 구하는 함수
def get_min_classroom(lectures):
lectures.sort() # 오름차순 정렬
pq = [-1] # 처음 인덱스 에러를 피하기 위해 음수 값 삽입. (첫번째 강의 때 갱신될 값)
for start, end in lectures:
if pq[0] <= start: # 가장 빨리 끝나는 강의실을 사용할 수 있는 경우
hq.heappop(pq)
hq.heappush(pq, end) # 끝나는 시간 삽입
return len(pq) # pq의 길이가 사용 중인 강의실의 수가 된다.
n = int(input())
lectures = [tuple(map(int, input().split())) for _ in range(n)]
# 연산 & 출력
print(get_min_classroom(lectures))
- 입력은 tuple 형태로 입력받기.(요소 값을 바꿀 수 없는 것은 빼고 리스트와 큰 차이가 없다...왜 굳이 큐플로 했지?)
- 일단 강의를 (첫번째 원소, 강의 시작시간)오름차순으로 정렬한다.
- 그 후 강의를 차례로 돌며 힙 구조에 최근에 삽입한 값(pop되는 값)이 강의 시작 시간보다 같거나 작으면 강의실을 사용할 수 있는 것이다. 그렇다면 해당 수를 pop한 후 end값을 삽입한다.
- 즉, 이전 강의의 end시간이 다음 강의의 start 시간보다 작거나 같으면 pop 한 후 다음 강의의 end 시간을 삽입하는 것이다.
- 해당 힙의 길이가 사용 중인 강의실의 수가 된다.
3. 3*N 타일
https://school.programmers.co.kr/learn/courses/30/lessons/12902
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
# 정답 풀이
MOD = 1000000007
def solution(n):
# n이 홀수인 경우는 만들 수 없음
if n % 2 != 0:
return 0
dp = [0] * (n + 1) # dp 배열 생성
dp[0] = 1 # dp[0]=1로 초기화해줘야 안쪽 for문을 돌면서 마지막에 dp[0]*2로 매번 추가되는 케이스가 더해짐.
dp[2] = 3
for i in range(4, n + 1, 2):
dp[i] = dp[i - 2] * 3
for j in range(i - 4, -1, -2):
dp[i] += dp[j] * 2
dp[i] %= MOD
return dp[n]
# 다른 풀이
def solution(n):
answer = 0
dp = [3,11]
if n <= 4: return dp[n//2-1]
else:
for i in range(2,n//2):
dp.append((dp[i-1]*4-dp[i-2])%1000000007)
return dp[-1]
- 적혀있는 것처럼 n이 홀수면 만들 수 없다.
- dp(dynamic programming) 문제. 문제를 작게 나누어서 푸는 문제이다. 대표적인게 피보나치 함수!
- n=2이면 answer=3, n=4이면 answer = 11, n=6이면 answer = 41, n=8이면 answer = 153
- 즉, 점화식 f(n) = f(n-2)*4 - f(n-4)이 나온다.
- 정답 풀이에서는 f(n) = f(n-2)*3 + f(n-4) + f(n-6) + ... + f(0) 이다. 다른 풀이가 더 쉬워서 참고함.
4. 외판원 순회2
https://www.acmicpc.net/problem/10971
10971번: 외판원 순회 2
첫째 줄에 도시의 수 N이 주어진다. (2 ≤ N ≤ 10) 다음 N개의 줄에는 비용 행렬이 주어진다. 각 행렬의 성분은 1,000,000 이하의 양의 정수이며, 갈 수 없는 경우는 0이 주어진다. W[i][j]는 도시 i에서 j
www.acmicpc.net
import sys
input = sys.stdin.readline
INF = 1e8
visited = [] # 방문 검사 배열
matrix = [] # 비용 행렬
ans = INF # 최소 비용 저장 위해 미리 최댓값으로 초기화
def backtracking(n, cnt, cur_city, cost):
global ans, visited, matrix
if cost >= ans:
return
if cnt == n: # 종료 조건 : n개의 도시를 순회했음
# 출발 도시(0)로 다시 돌아올 수 있는지 확인
if matrix[cur_city][0] != 0:
ans = min(ans, cost + matrix[cur_city][0])
return
for i in range(n): # cur_city에서 도시 i로 이동
if matrix[cur_city][i] and not visited[i]: # 길이 있고, 아직 방문하지 않은 도시
visited[i] = True
backtracking(n, cnt + 1, i, cost + matrix[cur_city][i])
visited[i] = False
def solution(n, cost):
global ans, visited, matrix
visited = [False] * n
matrix = cost
# 연산
visited[0] = True
backtracking(n, 1, 0, 0)
return ans
# 한 사이클을 만들면, 어느 도시에서 시작하든 값은 같으므로 0번 도시부터 시작하는 경우만 검사하면 된다.
# 입력
n = int(input())
cost = [list(map(int, input().split())) for _ in range(n)]
answer = solution(n, cost)
print(answer)
- 비용은 행렬로 주어진다. 즉, 1번째 줄 2번째 수가 10이면 1번 도시에서 2번 도시로 가는데 10만큼의 비용이 드는 것!
- 먼저 해당 도시를 방문했는지 검사하는 배열 visitied, 비용 행렬 matrix, 최소 비용을 저장할 변수 ans(처음에는 최댓값으로 초기화)를 선언한다. 모두 global로!
- backtracking 함수에서, n개의 도시를 순회한 경우 이거나 cost가 최댓값을 넘으면 종료한다.(검사할 필요가 없으므로)
- backtracking 함수로는 순회할 도시의 수 n, 현재 순회한 도시의 수 cnt, 현재 도 cur_city, 비용 행렬 cost를 넘겨준다.
- 만약 종료 조건에서 현재 도시에서 첫번째 도시, 즉 matrix[cur_city][0]으로 가는 비용 함수가 0이 아니라면(갈 수 있는 경우) ans와 지금까지의 cost(마지막 도시까지의 비용을 합친 비용)를 비교 후 최소 값을 저장한다.
- 종료 조건이 아닌경우(대부분 도시를 다 돌지 않은 경우) 현재 도시에서 i번째 도시까지의 matrix[cur_city][i](길이 있는지)와 visited[i](방문하지 않았는지)를 검사한다. 길이 있고 방문하지 않았다면 visitied는 True로, 바꾼 후 backtracking 호출!
- backtracking(n, cnt+1, i, cost+matrix[cur_city][i])를 통해 현재 도시 검사 후 다음 도시로 이동하게 된다. 즉, cnt==n이 될때까지 함수를 호출하며 cur_city도 i부터 n까지 반복 하게 됨! cost는 계속 증가함.
# 회고
<중> 문제부터는 못풀겠다...ㅎ 기출 달달 외워 가야지..
제대로 된 DFS문제 같은 것도 안나왔는데 여기서 막혔다ㅜ 어려워요
학교 졸업하기가 왤케 빡센지
'코딩테스트' 카테고리의 다른 글
[Python] ECC 2주차 정리 - 정렬 (1) | 2024.03.29 |
---|---|
[Python] ECC 1주차 정리 - 자료구조 (2) | 2024.03.22 |
[Python] 2024-1 이퍼 준비(1) (0) | 2024.03.19 |
[python] 프로그래머스 도전~ (0) | 2024.03.18 |
[python] 구현(완전 탐색, 시뮬레이션) (1) | 2024.02.24 |