├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ └── daily-coding-problem.md
└── PULL_REQUEST_TEMPLATE.md
├── BOJ
├── 1,2,3더하기
│ └── wooyeol.py
├── 2048 (Easy)
│ ├── jisu.py
│ ├── wooyeol.py
│ └── wooyeol2.py
├── LCS
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── List of Unique Numbers
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── N-Queen
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── Z
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 가장 긴 짝수 연속한 부분 수열 (large)
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 강의실 배정
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── sumin_2.py
│ └── wooyeol.py
├── 골드바흐의 추측
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 공유기 설치
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 괄호의 값
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 덱
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 로봇 청소기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 문자열 지옥에 빠진 호석
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 뱀
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 불!
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 빙산
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── wooyeol2.py
├── 쇠막대기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 수 이어 쓰기 2
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 스택 수열
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 싸이버개강총회
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 암호 만들기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 에디터
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 연속합
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 예산
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 잃어버린 괄호
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 제로
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 종이의 개수
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 카드2
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 큐
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
└── 키로거
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── CodeTree
├── README.md
└── 메이즈 러너
│ ├── jisu.py
│ └── wooyeol.py
├── Programmers
├── README.md
├── k진수에서 소수 개수 구하기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── n진수 게임
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 게임 맵 최단거리
│ ├── dohyun .py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 네트워크
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 단어 변환
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 더 맵게
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 두 원 사이의 정수 쌍
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 뒤에 있는 큰 수 찾기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 등굣길
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 땅따먹기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 롤케이크 자르기
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 모음사전
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 방문 길이
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 스킬트리
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 아방가르드 타일링
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 압축
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 야근 지수
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 연속된 부분 수열의 합
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 오픈채팅방
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 요격 시스템
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 이모티콘 할인행사
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 이중우선순위큐
│ ├── jisu.py
│ ├── jisu_baekjoon.py
│ ├── sumin.py
│ └── wooyeol.py
├── 전화번호 목록
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 정수 삼각형
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 주식가격
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 주차 요금 계산
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 최고의 집합
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 타겟 넘버
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 택배 배달과 수거하기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 파일명 정렬
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
├── 표 병합
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 표현 가능한 이진트리
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ ├── wooyeol.py
│ └── yerim.py
├── 프렌즈4블록
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
└── 피로도
│ ├── jisu.py
│ ├── sumin.py
│ ├── sumin2.py
│ ├── sumin3.py
│ └── wooyeol.py
├── README.md
├── Softeer
├── README.md
├── 순서대로 방문하기
│ ├── dohyun.py
│ ├── jisu.py
│ ├── sumin.py
│ └── wooyeol.py
└── 자동차 테스트
│ ├── dohyun.py
│ ├── jisu.py
│ ├── jisu_dfs.py
│ ├── sumin.py
│ └── wooyeol.py
└── ✨ 효과 만점 코딩테스트 Cheat Sheet!
├── ✏️ 파이썬에서 사용할 수 있는 유용한 구문 모음.md
├── 🍯 코드를 줄여줄 꿀 라이브러리 모음.md
├── 📚 파이썬 자료형 특징 모음.md
└── 🚀 알고리즘 해킹 아이디어.md
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @Woo-Yeol @ksumini @zsmalla
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/daily-coding-problem.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Weekly Coding Problem
3 | about: Assign weekly coding problem as issue.
4 | title: "[Curriculum] Problem_Name"
5 | labels: ''
6 | assignees: da-in
7 |
8 | ---
9 |
10 | ## Summary
11 |
12 |
13 | Date |
14 | 2023-00-00 |
15 |
16 |
17 | Difficulty |
18 | If there is a difficulty level presented by the site, please fill it out. |
19 |
20 |
21 | Link |
22 | https://link.example
23 | |
24 |
25 |
26 |
27 | ## Insight
28 |
29 | 스터디를 진행하며 얻은 인사이트를 정리합니다.
30 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### PR Summary
2 |
3 |
4 | ### ISSUE NUMBER
5 |
6 | - #0
7 | - #0
8 |
--------------------------------------------------------------------------------
/BOJ/1,2,3더하기/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 1,2,3 더하기
3 | https://www.acmicpc.net/problem/9095
4 |
5 | 어떤 알고리즘으로 풀이할 수 있을까? DP
6 |
7 | 1을 1,2,3의 합으로 나타내는 방법의 수 -> D[1]
8 | (1) : 1
9 | 2을 1,2,3의 합으로 나타내는 방법의 수 -> D[2]
10 | (1,1),(2) : 2
11 | 3을 1,2,3의 합으로 나타내는 방법의 수 -> D[3]
12 | (1,1,1),(1,2),(2,1),(3) : 4
13 | """
14 | import sys
15 |
16 | input = sys.stdin.readline
17 |
18 |
19 | def algorithm(dp: list):
20 | for i in range(4, 11):
21 | dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1]
22 | # 4 : 1,2,3
23 | # 5 : 2,3,4
24 | # 6 : 3,4,5
25 | return dp
26 |
27 |
28 | def main():
29 | T = int(input())
30 | dp = [0, 1, 2, 4] + [0 for _ in range(7)]
31 | dp = algorithm(dp)
32 | for _ in range(T):
33 | # n은 양수이며 11보다 작다.
34 | print(dp[int(input())])
35 |
36 |
37 | main()
38 |
--------------------------------------------------------------------------------
/BOJ/LCS/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - N 이 1000 이하 -> 최대 O(N^2) 으로 접근
8 | - 부분수열은 계속해서 중첩됨 -> DP 를 활용해보자
9 | - 두 문자열의 길이가 같지 않을 수 있음을 주의해야함
10 |
11 | - 틀린 풀이
12 | - 멀리 떨어져있는 값들을 어떻게 조각처럼 붙힐 수 있을까 ..?
13 | - 부분수열을 찾을 때 조합을 활용할 수 있을 것 같음, 다만 시간복잡도...
14 | - 조합을 활용할 때 메모이제이션을 이용할 수 있을까..? -> 모르겠음
15 | - 정답 풀이
16 | - 이중반복문을 활용하여 DP 테이블을 계산
17 | - 각 문자열을 순회하며 같은 문자를 확인
18 | - dp[i][j] 는 s1[0:i] 와 s2[0:j] 의 LCS 의 길이를 나타냄
19 |
20 | 회고
21 | - 점화식에 대해서 너무 복잡하게 생각한 것 같음
22 | - 사전에 점화식에 대한 고민을 충분히 해보고, 도저히 모르겠으면 일단 저퀄(?)의 코드로 구현을 먼저한 후 메모이제이션을 활용해 개선하는 방안도 필요할 것 같다
23 |
24 | """
25 |
26 | import sys
27 |
28 | inputs = sys.stdin.readline
29 | s1 = inputs().rstrip()
30 | s2 = inputs().rstrip()
31 |
32 | len_1, len_2 = len(s1), len(s2)
33 |
34 | dp = [[0] * (len_2 + 1) for _ in range(len_1 + 1)] # DP 테이블 초기화
35 |
36 | # 문자열을 한 문자씩 비교하면서 LCS 의 길이를 계산
37 | for i in range(1, len_1 + 1):
38 | for j in range(1, len_2 + 1):
39 | if s1[i - 1] == s2[j - 1]: # 공통부분 수열을 만들 수 있다면 만들기 (+1)
40 | dp[i][j] = dp[i - 1][j - 1] + 1
41 | else:
42 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) # 부분 수열을 만들 수 없다면 이전까지 기억한 부분수열 중 가장 긴것을 저장
43 |
44 | print(dp[-1][-1])
--------------------------------------------------------------------------------
/BOJ/LCS/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-10 18:50
3 |
4 | - st1과 st2 각각의 문자에 대응하는 matrix(dp 테이블)를 그리기
5 | -----------------
6 | _ A C A Y K P
7 | C 0 1 1 1 1 1 -> A C A Y K P 에서 C 까지 고려했을 때 LCS 길이
8 | A 1 1 2 2 2 2 -> A C A Y K P 에서 C A 까지 고려했을 때 LCS 길이
9 | P 1 1 2 2 2 3
10 | C 1 2 2 2 2 3
11 | A 1 2 3 3 3 3
12 | K 1 2 3 3 4 4 -> A A C Y K P 에서 C A P C A K 까지 고래했을 때 LCS 길이
13 | -----------------
14 | - 규칙이 보임
15 | - [r][c]에 대응하는 두 문자열의 문자가 같을 경우 왼쪽 위 대각선([r-1][c-1])에서 +1이 증가함
16 | - [r][c]에 대응하는 두 문자열의 문자가 다를 경우 왼쪽([r][c-1]), 위쪽([r-1][c]) 중 큰 값이 옴
17 | - r, c가 각각 0일 때에 대한 예외 처리
18 | - 그냥 임시로 행, 열 하나씩 늘려서 처리하면 동일하게 로직 적용 가능
19 |
20 | 풀이 완료 : 2023-09-10 19:45(풀이 시간 : 55분)
21 |
22 | """
23 |
24 | import sys
25 |
26 | input = sys.stdin.readline
27 |
28 | st1 = input().rstrip()
29 | st2 = input().rstrip()
30 |
31 | # 행과 열이 0일 때의 예외처리를 위해 임시로 행 열 하나씩 늘리기
32 | dp = [[0 for _ in range(len(st1) + 1)] for _ in range(len(st2) + 1)]
33 |
34 | for r in range(1, len(st2) + 1):
35 | for c in range(1, len(st1) + 1):
36 | if st2[r - 1] == st1[c - 1]: # 대응하는 문자가 같을 경우
37 | dp[r][c] = dp[r - 1][c - 1] + 1 # 대각선에서 증가
38 | else: # 대응하는 문자가 다를 경우
39 | dp[r][c] = max(dp[r - 1][c], dp[r][c - 1]) # 왼쪽, 위쪽 중 큰게 옴
40 |
41 | print(dp[-1][-1])
42 |
--------------------------------------------------------------------------------
/BOJ/LCS/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 35분
3 |
4 |
5 | 두 문자열
6 | - 문자열은 알파벳 대문자(최대 1000글자)
7 |
8 |
9 | d[i][j]: word1의 i까지, word2의 j까지 있을 때 LCS(최장 공통 부분 수열)의 길이
10 | 1) word1[i] == word2[j]
11 | - d[i][j] = d[i-1][j-1] + 1
12 |
13 | 2) word1[i] != word2[j]
14 | - d[i][j] = max(d[i-1][j], d[i][j-1])
15 |
16 | <시간 복잡도>
17 | o(N^2)
18 | """
19 | import sys
20 | input = sys.stdin.readline
21 |
22 |
23 | word1 = input().rstrip()
24 | word2 = input().rstrip()
25 |
26 | h, w = len(word1), len(word2)
27 | d = [[0] * (w+1) for _ in range(h+1)]
28 |
29 | for i in range(1, h+1):
30 | for j in range(1, w+1):
31 | if word1[i-1] == word2[j-1]:
32 | d[i][j] = d[i-1][j-1] + 1
33 | else:
34 | d[i][j] = max(d[i][j-1], d[i-1][j])
35 | print(d[h][w])
--------------------------------------------------------------------------------
/BOJ/LCS/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | LCS
3 | https://www.acmicpc.net/problem/9251
4 |
5 | 풀이 참조 : https://velog.io/@emplam27/알고리즘-그림으로-알아보는-LCS-알고리즘-Longest-Common-Substring와-Longest-Common-Subsequence
6 |
7 | 풀이시간
8 | 13:22 ~ 14:22(1시간 고민 후 풀이 참고)
9 |
10 | 문제 조건
11 | 문자열의 최대 길이는 1000
12 |
13 | 시간 복잡도 :
14 | O(N * N) = O(N^2)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> DP
18 |
19 | 점화식:
20 | 1. 문자열A, 문자열B의 한글자씩 비교해봅니다.
21 | 2. 두 문자가 다르다면 LCS[i - 1][j]와 LCS[i][j - 1] 중에 큰값을 표시합니다.
22 | 3. 두 문자가 같다면 LCS[i - 1][j - 1] 값을 찾아 +1 합니다.
23 | 4. 위 과정을 반복합니다.
24 |
25 | """
26 | import sys
27 |
28 | input = sys.stdin.readline
29 |
30 | s1 = input().rstrip()
31 | s2 = input().rstrip()
32 |
33 | s1_length = len(s1)
34 | s2_length = len(s2)
35 |
36 | dp = [[0] * (s2_length + 1) for _ in range(s1_length + 1)]
37 |
38 | for i in range(1, s1_length + 1):
39 | for j in range(1, s2_length + 1):
40 | # 점화식을 코드로 옮긴 내용
41 | if s1[i - 1] == s2[j - 1]:
42 | dp[i][j] = dp[i - 1][j - 1] + 1
43 | else:
44 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
45 |
46 | print(dp[-1][-1])
47 |
--------------------------------------------------------------------------------
/BOJ/List of Unique Numbers/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간
5 |
6 | 접근법
7 | - N 십만 이하 -> O(NlogN)
8 | - 연속된 수의 개수를 1개, 2개, ... , N 개 뽑기, 최소값은 연속한 1개의 수를 뽑았을 때 -> N
9 | - 이를 투포인터로 구현 가능
10 | - 집합 자료형을 통해 고유값만을 갖는 수열을 구해놓고, 해당 수열의 길이만큼 빼주면 됨
11 | - ex: [1,2,3,4,5]
12 | - cnt += end - start (5-0 = 5)
13 | - tmp_set = [1,2,3,4,5] -> [1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5] (cnt 개수 = 경우의 수)
14 | - 이후에 start 포인트를 제거 -> tmp_set = [2,3,4,5]
15 | - cnt += end - start (5-1 = 4)
16 | - tmp_set = [2,3,4,5] -> [2], [2,3], [2,3,4], [2,3,4,5]
17 | - ...
18 | - cnt += end - start (5-4 = 1)
19 | - tmp_set = [5] -> [5]
20 | - start 포인트 제거 -> tmp_set = []
21 | - cnt += end - start (5-5 = 0)
22 | - 종료
23 |
24 | 회고
25 | - 좀 더 많이 푸는 연습을 해서 버벅거리는 시간 줄이기
26 |
27 | """
28 |
29 | import sys
30 |
31 | inputs = sys.stdin.readline
32 |
33 | N = int(inputs().rstrip())
34 | arr = list(map(int, inputs().split()))
35 |
36 | cnt, end = 0, 0
37 | tmp_set = set()
38 |
39 | for start in range(N):
40 | while (end < N) and (arr[end] not in tmp_set): # end 인덱스가 범위 내이고, arr[end] 값이 unique 값일 때
41 | tmp_set.add(arr[end])
42 | end += 1
43 |
44 | cnt += end - start # end 와 start 의 차이만큼이 고유값을 갖는 리스트의 개수
45 | tmp_set.discard(arr[start])
46 |
47 | print(cnt)
--------------------------------------------------------------------------------
/BOJ/List of Unique Numbers/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-27 10:28
3 |
4 | #### 제한 사항
5 | N <= 100,000 이므로 O(NlogN) 이하의 알고리즘을 설계해야 한다.
6 |
7 | #### 풀이
8 | - 투 포인터로 접근 (`low, high = 0, 0`)
9 | - high 인덱스의 값이 현재 sub_set 집합에 없으면
10 | - sub_set 집합에 high 인덱스의 값을 add
11 | - 이후 high를 넓혀서 탐색
12 | - 경우의 수 1 증가
13 | - 집합에 이미 있는 경우 같은 수가 하나의 경우에 여러번 나타난 것
14 | - sub_set을 초기화 하고 low, high를 low+1로 초기화
15 |
16 | 풀이 중단 : 2023-09-27 11:00 (32분 경과)
17 |
18 | #### 풀이 재개 : 2023-09-27 14:30
19 |
20 | #### 풀이(스터디 이후)
21 | - low번째 인덱스부터 중복 없는 high인덱스까지의 경우의 수는 그 구간의 길이임
22 | - 예를 들어 1 2 3 1 2 에서 중복 없는 1 2 3 구간에서 1부터 시작하는 경우의 수는 1, 12, 123 => 3가지이므로 구간의 길이
23 | - 따라서 low로 순회하면서, high를 low와 중복되지 않는 인덱스까지 넓히며 탐색을 반복하면 된다.
24 | - 1 2 3 -> 2 3 1 -> 3 1 2 -> 1 2 -> 2 : 12개의 경우의 수
25 |
26 | 메모리 | 시간
27 | -- | --
28 | 44844KB | 152ms
29 |
30 |
31 | """
32 |
33 | import sys
34 |
35 | input = sys.stdin.readline
36 |
37 | N = int(input())
38 | S = input().rstrip().split()
39 |
40 | answer = 0
41 | sub_set = set()
42 |
43 | low, high = 0, 0
44 |
45 | while low < N:
46 | if high < N and S[high] not in sub_set: # 중복 발생 전까지 high 넓히기
47 | sub_set.add(S[high]) # 중복을 고려하기 위함
48 | high += 1
49 | else:
50 | answer += high - low # 중복 발생 or high 인덱스 초과 시 구간의 길이가 경우의 low부터 시작하는 경우의 수
51 | if high < N: # 중복 발생 시
52 | sub_set.remove(S[low]) # low의 수를 제거해 다음 인덱스 순회
53 | low += 1
54 |
55 | print(answer)
56 |
--------------------------------------------------------------------------------
/BOJ/List of Unique Numbers/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 30분
3 |
4 |
5 | n : 수열의 길이 (1 ≤ N ≤ 100,000)
6 | s : 수열 (1 ≤ 원소 ≤ 100,000)
7 |
8 |
9 | 투포인터를 통해 연속된 부분 수열을 만들 수 있다.
10 | 수를 하나씩 확인하며 딕셔너리에 해당 숫자가 마지막으로 나온 인덱스를 보관해 중복되지 않은 부분 수열을 만든다.
11 | 예를 들어, [1, 2, 3, 2, 1]이라는 수열이 있을 때
12 | 각 수를 탐색하며 아직 나오지 않은 수라면 딕셔너리에 해당 수의 인덱스를 추가해준다.
13 | 이전에 등장한 적이 있고, 딕셔너리에 저장된 인덱스값이 left 보다 크다면 left 인덱스를 1 증가시켜서 이전에 등장한 수의 인덱스보다 한 칸 오른쪽으로 이동한다.
14 | left = 0, right = 0 -> {1: 0}, ans = 1
15 | left = 0, right = 1 -> {1: 0, 2: 1}, ans = 1 + 2
16 | left = 0, right = 2 -> {1: 0, 2: 1, 3: 2}, ans = 1 + 2 + 3
17 | left = 2, right = 3 -> {1: 0, 2: 3, 3: 2}, ans = 1 + 2 + 3 + 2
18 | left = 2, right = 4 -> {1: 4, 2: 3, 3: 2}, ans = 1 + 2 + 3 + 2 + 3
19 |
20 | <시간복잡도>
21 | O(n)
22 | """
23 |
24 | import sys
25 | input = sys.stdin.readline
26 |
27 |
28 | n = int(input())
29 | s = list(map(int, input().split()))
30 |
31 | # 숫자의 등장 여부 및 인덱스를 저장하기 위한 딕셔너리
32 | appeared = {}
33 | left = 0 # 왼쪽 포인터(수열의 시작 인덱스)
34 | result = 0
35 |
36 | for right in range(n):
37 | cur_num = s[right]
38 | # 현재 숫자가 등장한 적이 있고, 앞서 나온 위치가 왼쪽 포인터보다 크거나 같은 경우
39 | if cur_num in appeared and appeared[cur_num] >= left:
40 | left = appeared[cur_num] + 1 # 이전에 등장했던 같은 수의 인덱스보다 한 칸 오른쪽으로 이동
41 | appeared[cur_num] = right # 딕셔너리에서 인덱스 갱신
42 | # 현재 부분 수열의 길이를 계산하여 결과에 더함
43 | result += (right - left + 1)
44 |
45 | print(result)
--------------------------------------------------------------------------------
/BOJ/N-Queen/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 25분
3 |
4 |
5 | - N(1 ≤ N < 15)
6 |
7 |
8 | - N X N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓기 위해서는 각 행에 1개의 퀸을 놔야 한다.
9 | - 퀸은 상하좌우, 대각선으로 움직이기 때문에 위에서부터 아래로 내려오며 퀸을 배치할 수 있는 곳에 배치해야 한다.
10 | - 모든 경우의 수를 확인하되, 퀸을 둘 수 없는 칸(상하좌우, 대각선 칸에 이미 다른 퀸이 있는 경우)에 대해 처리하기 때문에 백트래킹으로 풀이가 가능하다.
11 | 1) 같은 열인지 확인: 같은 col을 가질 때
12 | 2) 같은 ↗ 대각선에 있는지 확인: row + col값이 같을 때
13 | 3) 같은 ↘ 대각선에 있는지 확인: row - col값이 같을 때
14 |
15 | <시간복잡도>
16 | 열만 가지고 생각했을 때는 O(N!)일 수 있지만, 실제 백트래킹에서 가지치기가 빈번하게 발생하면 이보다 훨씬 시간복잡도가 줄어들기 때문에 제대로 파악하기 어려움
17 | """
18 | n = int(input())
19 | check_col = [False] * n # 같은 열에 다른 퀸이 있는지 확인하기 위한 배열
20 | check_dig = [False] * (2*n-1) # ↗ 대각선을 확인하기 위한 배열: n x n 사각형의 대각선 개수는 (2*n-1)개
21 | check_dig2 = [False] * (2*n-1) # ↘ 대각선을 확인하기 위한 배열: n x n 사각형의 대각선 개수는 (2*n-1)개
22 |
23 | def go(row: int) -> int:
24 | if row == n: # 마지막 행까지 퀸을 모두 배치하면 종료
25 | return 1
26 | ans = 0 # (서로 공격할 수 없게) n개의 퀸을 놓을 수 있는 모든 경우의 수
27 | for col in range(n): # (row, col)에 퀸을 놓기
28 | # 같은 열에 퀸이 있거나, ↗, ↘ 대각선에 퀸이 있는 경우 퀸을 둘 수 없음(가지치기)
29 | if check_col[col] or check_dig[row+col] or check_dig2[row-col+n-1]:
30 | continue
31 | check_col[col] = True # (row, col)과 같은 열에 모든 칸에 퀸을 둘 수 없음
32 | check_dig[row+col] = True # (row, col)의 ↗ 대각선에 있는 모든 칸에 퀸을 둘 수 없음
33 | check_dig2[row-col+n-1] = True # (row, col)의 ↘ 대각선에 있는 모든 칸에 더 이상 퀸을 둘 수 없음
34 | ans += go(row+1)
35 |
36 | # 백트래킹
37 | check_col[col] = False
38 | check_dig[row+col] = False
39 | check_dig2[row-col+n-1] = False
40 |
41 | return ans
42 |
43 | print(go(0))
--------------------------------------------------------------------------------
/BOJ/Z/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 25분
3 |
4 |
5 | - 1 ≤ N ≤ 15
6 | - 0 ≤ r, c < 2N
7 |
8 |
9 | 1. 함수의 정의
10 | - 2^n x 2^n 배열에서 (r,c)를 방문하는 순서를 반환하는 함수
11 |
12 | 2. base condition
13 | - n = 0일 때, return 0
14 |
15 | 3. 재귀식
16 | - (r,c)가 1번 사각형일 때, return go(n-1, r, c)
17 | - (r,c)가 2번 사각형일 때, return half*half + go(n-1, r, c-half)
18 | - (r,c)가 3번 사각형일 때, return 2*half*half + go(n-1, r-half, c)
19 | - (r,c)가 4번 사각형일 때, return 3*half*half + go(n-1, r-half, c-half)
20 |
21 | """
22 | def go(n, r, c):
23 | if n == 0:
24 | return 0
25 | half = 2 ** (n-1)
26 | if r < half and c < half: # 1번 사각형
27 | return go(n-1, r, c)
28 | elif r < half and c >= half: # 2번 사각형
29 | return half * half + go(n-1, r, c-half)
30 | elif r >= half and c < half: # 3번 사각형
31 | return 2 * half * half + go(n-1, r-1, c)
32 | return 3 * half * half + go(n-1, r-half, c-half) # 4번 사각형
33 |
34 | n, r, c = map(int, input().split())
35 | print(go(n, r, c))
--------------------------------------------------------------------------------
/BOJ/Z/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | Z
3 | https://www.acmicpc.net/problem/1074
4 |
5 | 풀이시간 07:00 ~ 07:25(25분)
6 |
7 | 1 <= N <= 15
8 | 0 <= r,c < 2**N
9 |
10 | 접근법
11 | 무슨 알고리즘으로 풀이 할 수 있을까? 재귀적 구현
12 |
13 | 제 4사분면으로 분리하고 각 사분면의 시작점을 기준으로 이전 값들을 더해주는 연산을 진행한다면
14 | 재귀로 편하게 나타낼 수 있다.
15 |
16 | 그렇기에 BaseCondition은 N이 0일 때 0을 반환하게 한다.
17 |
18 | 가장 작은 N은 0이고 이는 r,c에 관계없이 순서가 0번째임을 나타내는 것이다.
19 |
20 | 그렇다면 N == 1이라면,
21 | size = 2 **(n-1)일때 size=1
22 | x * (1 * 1) + 0 이다. (x는 사분면 - 1)
23 | 이다.
24 |
25 | """
26 | import sys
27 |
28 | input = sys.stdin.readline
29 |
30 |
31 | def recursive(n: int, r: int, c: int):
32 | # Base Condition
33 | if n == 0:
34 | return 0
35 |
36 | size = 2 ** (n - 1)
37 |
38 | # print(n, size, r, c)
39 |
40 | # 제 2사분면
41 | if r < size and c < size:
42 | return 0 * (size * size) + recursive(n - 1, r, c)
43 |
44 | # 제 1사분면
45 | if r < size and c >= size:
46 | return 1 * (size * size) + recursive(n - 1, r, c - size)
47 |
48 | # 제 3사분면
49 | if r >= size and c < size:
50 | return 2 * (size * size) + recursive(n - 1, r - size, c)
51 |
52 | # 제 4사분면
53 | if r >= size and c >= size:
54 | return 3 * (size * size) + recursive(n - 1, r - size, c - size)
55 |
56 |
57 | N, r, c = map(int, input().split())
58 |
59 | print(recursive(N, r, c))
60 |
--------------------------------------------------------------------------------
/BOJ/가장 긴 짝수 연속한 부분 수열 (large)/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 40분 풀이 -> 도저히 시작이 안되서 풀이 참고
5 |
6 | 접근법
7 | - N 백만 -> O(NlogN) ~ O(N) 복잡도로 해결
8 | - 짝수로 이루어져있는 연속한 부분 수열의 가장 긴길이를 만들려면?
9 | - 짝수들이 밀집되어 있는 수열의 중간에 낀 홀수들을 삭제해야함
10 | - 인덱스별 짝수 여부를 저장해놓으면 유용할 것
11 | - 삭제를 진행해주어야 하기 때문에 시간복잡도 측면에서 리스트 대신 딕셔너리 사용
12 | - 이 때 해당 인덱스 까지의 짝수 개수를 저장해놓으면 어떨까?
13 |
14 | - 풀이 실패로 답지 참조
15 | - 투 포인터를 활용 -> 특정 조건을 만족하는 부분 수열의 탐색을 낮은 시간복잡도로 풀이 가능
16 | - 핵심은 홀수의 개수가 K개(최대 제거가능한 횟수)가 될때까지 탐색하는 것
17 |
18 | 회고
19 | - 떠올리기 어려운 문제가 아니었음에도 불구하고 투포인터 자체를 생각도 못했다 ㅠㅠ
20 | - 특정 조건을 만족하는 부분 수열을 찾는 문제는 투포인터를 생각해보는게 좋을 듯
21 | - 가장 낯선 유형 중 하나인데 문제 많이 풀어보기
22 |
23 | """
24 |
25 | import sys
26 | input = sys.stdin.readline
27 |
28 | n, k = map(int, input().split())
29 | S = list(map(int, input().split()))
30 |
31 | cnt = 0 # 홀수의 개수
32 | start, end = 0, 0
33 | size, size_max = 0, 0 # 현재 수열의 길이, 최대 수열의 길이
34 | flag = True
35 |
36 | for start in range(n): # 투포인터 탐색 시작
37 | while cnt <= k and flag:
38 | if S[end] % 2:
39 | if cnt == k: # 만약 홀수가 K 개라면 탐색 멈춤 -> K 개의 홀수 제거
40 | break
41 | cnt += 1
42 | size += 1 # 늘어난 현재 수열의 길이
43 |
44 | if end == n - 1: # 최대 탐색이 끝났다면 탐색 멈춤
45 | flag = False
46 | break
47 |
48 | end += 1 # end 인덱스 증가
49 |
50 | size_max = max(size-cnt, size_max) # 홀수개를 제거했을 때의 임시 수열의 길이의 최대값
51 |
52 | # start 를 한 칸 뒤로 이동시키는 작업
53 | if S[start] % 2: # start가 홀수가 아니라면 홀수의 개수 감소
54 | cnt -= 1
55 |
56 | size -= 1 # start 를 수열에서 제거 (뒤로 이동)
57 |
58 | print(size_max)
59 |
--------------------------------------------------------------------------------
/BOJ/가장 긴 짝수 연속한 부분 수열 (large)/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 50분
3 |
4 |
5 | n: 수열 s의 길이 (1 <= n <= 1,000,000)
6 | k: 삭제할 수 있는 최대 횟수 (1 <= k <= 100,000)
7 | s: 수열 (1 <= 원소의 값 <= 1,000,000)
8 |
9 |
10 | 최대 K개의 수를 삭제할 수 있다면, 연속한 짝수 부분수열의 길이를 길게 하기 위해 홀수만 K번 삭제를 해야 한다.
11 | -> 홀수를 K개 포함하는 부분수열 중에서 가장 긴 것을 찾는 문제
12 | 1. left와 right를 0부터 시작해 연속된 부분 수열을 탐색한다.
13 | 2. right를 이동하면서, 홀수가 나오면 해당 수를 제거한다(실제로 remove가 아니라 remove_cnt를 통해 개념적으로 제거)
14 | 3. 만약 remove_cnt가 제거할 수 있는 최대 개수인 K를 넘어선다면 left를 옮겨준다.
15 | 4. right를 끝까지 이동할 때까지, 가장 긴 연속된 짝수 부분 수열의 길이를 확인한다.
16 |
17 | <시간복잡도>
18 | O(n)
19 | - 두 포인터는 한 번에 하나의 요소만 확인하므로 모든 요소를 확인하는데 O(N)
20 | - while문에서 홀수를 삭제하려면 left 포인터를 최악의 경우 (remove_cnt - K)번 이동
21 |
22 | """
23 | import sys
24 | input = sys.stdin.readline
25 |
26 |
27 | n, k = map(int, input().split()) # n: 수열의 길이, k: 최대 삭제 횟수
28 | s = list(map(int, input().split())) # 수열 s
29 |
30 | max_len = 0 # 가장 긴 연속된 짝수 부분 수열의 길이
31 |
32 | left = 0 # 연속된 부분 수열의 왼쪽 포인터
33 | remove_cnt = 0 # 현재 연속된 부분 수열에서 삭제한 홀수의 개수
34 |
35 | for right in range(n): # 연속된 부분 수열의 오른쪽 포인터를 이동하며 검사
36 | # 현재 오른쪽 포인터가 가르키고 있는 수가 홀수라면, 해당 수를 제거
37 | if s[right] % 2 != 0:
38 | remove_cnt += 1
39 | # 제거한 홀수의 개수가 k를 넘으면, 왼쪽 포인터를 옮겨 연속 부분 수열의 길이를 줄여준다.
40 | if remove_cnt > k:
41 | if s[left] % 2 != 0:
42 | remove_cnt -= 1
43 | left += 1
44 | max_len = max(max_len, right-left+1-remove_cnt) # 현재 연속된 부분 수열의 길이를 갱신
45 |
46 | print(max_len)
--------------------------------------------------------------------------------
/BOJ/강의실 배정/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - N <= 이십만 -> 최대 O(NlogN) 복잡도로 해결
8 | - 최대한 겹치는 시간이 없게 해야 최소의 강의실을 사용
9 |
10 | - 틀린 풀이
11 | - K 시간동안 강의실이 몇개 있는지를 딕셔너리에 담아보자
12 | - 메모리초과..?
13 | - 정답 풀이
14 | - 정렬을 활용해서 수업의 시작과 끝을 체크하여 정답값을 갱신하는 형태
15 | - 시작하면 +1, 종료하면 -1, 정답값은 가장 많이 수업이 진행되고 있을 때 강의실 수
16 |
17 | 회고
18 | - 그동안 공간복잡도문제(메모리초과)가 있었던 적이 없어서 당황 ...
19 | - 종료시간 범위가 10^9 이라 리스트가 아닌 딕셔너리를 활용해 더욱 메모리를 아꼈다고 생각했는데 턱없이 부족했던 것 같음
20 | - 이런 유형(?)의 문제를 가끔 풀어봤던 것 같은데 항상 많이 취약했던 것 같음, 연습 하기!
21 |
22 | """
23 |
24 | # 틀린 풀이
25 | import sys
26 | from collections import defaultdict
27 |
28 | inputs = sys.stdin.readline
29 |
30 | n = int(inputs())
31 | arr = []
32 | for _ in range(n):
33 | arr.append(list(map(int, inputs().split())))
34 |
35 | times = defaultdict(int)
36 |
37 | for time in arr:
38 | s, e = time # start, end
39 | for i in range(s, e): # 끝나는 시간에는 수업시작가능 (따라서 e+1 가 아닌 e)
40 | times[i] += 1
41 |
42 | print(max(times.values()))
43 |
44 | # 정답 풀이
45 | import sys
46 |
47 | inputs = sys.stdin.readline
48 |
49 | n = int(inputs())
50 | events = []
51 |
52 | for _ in range(n):
53 | s, e = map(int, inputs().split())
54 | events.append((s, 1)) # 수업 시작 이벤트
55 | events.append((e, -1)) # 수업 종료 이벤트
56 |
57 | events.sort() # 이벤트를 시간 순서대로 정렬
58 |
59 | classrooms = 0 # 현재 사용 중인 강의실 개수
60 | max_classrooms = 0 # 최대로 필요한 강의실 개수
61 |
62 | for _, event_type in events:
63 | if event_type == 1: # 수업 시작 이벤트
64 | classrooms += 1
65 | max_classrooms = max(max_classrooms, classrooms)
66 | else: # 수업 종료 이벤트
67 | classrooms -= 1
68 |
69 | print(max_classrooms)
--------------------------------------------------------------------------------
/BOJ/강의실 배정/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-13 13:44
3 |
4 | #### 제한 사항
5 | - N <= 200,000 이므로, O(NlogN) 알고리즘을 설계해야 한다.
6 |
7 | #### 풀이
8 | - 일단 강의 시작 순서대로 주어진다는 조건이 없으므로 정렬 O(NlogN)
9 | - 모든 배치된 강의실 중 가장 빨리 끝나는 강의실보다 현재 진행되어야 하는 강의가 더 빠르면 강의실을 추가해야 함
10 | - 강의가 끝나는 시간을 담은 heap으로 강의실 배정 리스트를 관리한다.
11 | - 모든 강의를 순회하며 해당 강의의 시작하는 시간이 가장 빨리 끝나는 강의실(heap의 root)의 끝나는 시간보다 더 빠른지 체크
12 | - 더 빠르다면 힙에 해당 강의의 끝나는 시간 삽입 : 강의실을 추가한다는 의미
13 | - 가장 빨리 끝나는 강의실의 강의가 더 빨리 끝난다면 heappop 후, 삽입 : 해당 강의실에 강의를 교체한다는 의미
14 | - 순회 : O(N), heappush: O(logN) -> O(NlogN)
15 | - 최종적으로 O(2NlogN)으로 풀이 가능
16 |
17 | 풀이 완료 : 2023-09-13 14:50 (1시간 6분 소요)
18 | """
19 |
20 | import sys
21 | from heapq import heappush, heappop
22 |
23 |
24 | input = sys.stdin.readline
25 |
26 | N = int(input())
27 |
28 | classes = sorted(
29 | list(tuple(map(int, input().rstrip().split())) for _ in range(N))
30 | ) # 시작 시간 기준으로 정렬
31 | assigned = [] # min heap으로 사용
32 |
33 | for st, en in classes:
34 | if not assigned or assigned[0] > st: # 가장 빨리 끝나는 강의의 끝나는 시간보다 현재 강의 시작이 빠름
35 | heappush(assigned, en) # 그럼 강의실 추가해야 됨
36 | else: # 아니면
37 | heappop(assigned) # 기존 강의 끝내고
38 | heappush(assigned, en) # 현재 강의 배정
39 |
40 | print(len(assigned)) # 최종 배정한 강의실 개수 반환
41 |
--------------------------------------------------------------------------------
/BOJ/강의실 배정/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 38분
3 |
4 |
5 | - n: 수업의 개수
6 | - s(강의 시작 시간), t(강의가 끝나는 시간)가 n개 주어짐
7 |
8 |
9 | 수업이 아직 끝나지 않았다면 해당 강의실은 쓸 수가 없다.
10 | -> 현재 진행 중인 수업이 끝나야지만 그 강의실을 쓸 수 있다.
11 | 1. 가장 먼저 시작하는 강의의 강의가 끝나는 시간을 최소힙에 삽입
12 | 2. 두 번째로 가장 먼저 시작하는 강의부터 가장 늦게 시작하는 강의를 순회한다.
13 | - 현재 확인하고 있는 강의의 시작시간이 가장 일찍 끝나는 강의의 종료시간과 같거나 크다면, 해당 강의실을 그대로 사용할 수 있는 것이기 때문에 힙에서 종료시간을 갱신
14 | - 현재 확인하고 있는 강의의 시작시간이 가장 일찍 끝나는 강의보다 더 먼저 시작한다면, 강의실을 추가로 배정해야 하기 때문에 해당 강의의 끝나는 시간을 최소힙에 넣어줌
15 |
16 |
17 | <시간 복잡도>
18 | O(NlogN)
19 | """
20 | import sys
21 | input = sys.stdin.readline
22 | import heapq
23 |
24 |
25 | # 수업의 개수
26 | n = int(input())
27 |
28 | # (시작 시간, 끝나는 시간)을 원소로 갖는 리스트
29 | schedule = [tuple(map(int, input().split())) for _ in range(n)]
30 | schedule.sort() # 강의 시작 시간을 기준으로 오름차순 정렬(이후 끝나는 강의가 있으면 제일 빨리 시작하는 강의를 집어넣기 위해)
31 |
32 | assign = [] # 강의실 배정 상황(끝나는 시간)
33 | heapq.heappush(assign, schedule[0][1]) # 가장 일찍 시작하는 강의의 끝나는 시간을 최소힙에 집어넣기
34 |
35 | for s, e in schedule[1:]: # 두 번째로 일찍 시작하는 강의부터 가장 늦게 시작하는 강의까지 순회
36 | if s >= assign[0]: # 시작 시간이 가장 빨리 끝나는 회의실의 종료시간과 같거나 크다면 해당 강의실을 이어서 사용 가능
37 | heapq.heappop(assign)
38 | heapq.heappush(assign, e)
39 |
40 | print(len(assign))
--------------------------------------------------------------------------------
/BOJ/강의실 배정/sumin_2.py:
--------------------------------------------------------------------------------
1 | import sys
2 | input = sys.stdin.readline
3 |
4 | # 수업 개수
5 | n = int(input())
6 |
7 | # 수업 시작시간, 종료시간
8 | start, end = [], []
9 | for _ in range(n):
10 | a, b = map(int, input().split())
11 | start.append(a)
12 | end.append(b)
13 |
14 | # 각각 오름차순 정렬
15 | start.sort()
16 | end.sort()
17 |
18 | e = 0 # 현재까지 끝난 수업 중 가장 빨리 끝난 수업의 인덱스
19 | for s in start:
20 | # 종료 시간 <= 시작 시간이면(현재 수업이 이전 수업과 겹치지 않는다면)
21 | if end[e] <= s: # 현재 끝난 수업 중 가장 빨리 끝난 강의실을 이어서 사용하면 된다.
22 | e += 1 # 끝나는 시간이 가장 빠른 강의실의 인덱스를 업데이트 해준다.
23 | n -= 1 # 예약했던 강의실의 개수에서 하나 빼주면 된다.
24 | print(n)
--------------------------------------------------------------------------------
/BOJ/강의실 배정/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 강의실 배정
3 | https://www.acmicpc.net/problem/11000
4 |
5 | 풀이시간
6 | 13:18 ~ 13:34(16분)
7 |
8 | 문제 조건
9 | 과목 갯수 1 <= N <= 200,000
10 |
11 | 시간 복잡도 : O(NlogN)
12 | O(NlogN + NlogN) = O(NlogN)
13 |
14 | 접근법
15 | 무슨 알고리즘으로 풀이 할 수 있을까? -> heap 문제
16 |
17 | - 입력받는 과목의 시작 시간 순으로 정렬
18 | - 입력받은 과목를 하나씩 확인하며 강의실에 push
19 | - 현재 강의가 시작하는 시간 전에 강의가 끝났을 경우 모두 pop
20 | - 현재 사용하는 강의실이 최대값보다 많다면 업데이트
21 | """
22 | import sys
23 | from heapq import heappush, heappop
24 |
25 | input = sys.stdin.readline
26 |
27 | N = int(input())
28 |
29 | # 과목의 시작 순서로 정렬할 최소 힙 subjects
30 | subjects = []
31 |
32 | # 현재 강의중인 강의의 끝나는 시간을 정렬할 최소 힙 classes
33 | classes = []
34 |
35 | # 사용된 강의실의 최대 갯수를 업데이하기 위한 answer
36 | answer = 0
37 |
38 | # 과목의 시작 시간을 순서로 최소 힙 생성
39 | for _ in range(N):
40 | heappush(subjects, tuple(map(int, input().split())))
41 |
42 | while subjects:
43 | # 현재 수업을 해야하는 강의 pop
44 | c_start, c_end = heappop(subjects)
45 |
46 | # 현재 시간 업데이트
47 | time = c_start
48 |
49 | # 현재 강의중인 클래스에 해당 강의의 끝나는 시간 push
50 | heappush(classes, c_end)
51 |
52 | # 현재 시간에 강의가 끝나는 경우 모두 pop
53 | while classes[0] <= time:
54 | heappop(classes)
55 |
56 | # 현재 강의중인 강의실 갯수를 통해 최대값 갱신
57 | if answer < len(classes):
58 | answer = len(classes)
59 |
60 | print(answer)
--------------------------------------------------------------------------------
/BOJ/골드바흐의 추측/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분
5 |
6 | 접근법
7 | - n <= 10000 -> O(NlogN)
8 | - 에라토스테네스의 체? 아직 암기 못함 -> 업그레이드 버전 소수 판별로 가자..!
9 | - 작은 수의 소수를 left 라고했을 떄 number - left 가 소수이면 OK
10 | - 여러가지 경우가 나올걸 대비해 차이를 갱신
11 | - n 이 1만까지이므로 미리 소수를 구해놓고, set 자료형을 활용하여 시간복잡도 개선
12 |
13 | 회고
14 | - n 이 매우 컸다면 메모리초과 문제가 발생했을 것 같은 생각
15 | - 근데 에라토스테네스의 체도 마찬가지일 것 같음 (그냥 n이 매우 크게 주어지지가 않을듯!)
16 |
17 | """
18 |
19 | import sys
20 |
21 | def is_prime_number(n):
22 | if n < 2:
23 | return False
24 | for i in range(2, int(n**(1/2)) + 1):
25 | if n % i == 0:
26 | return False
27 | return True
28 |
29 | inputs = sys.stdin.readline
30 |
31 | all_prime_numbers = []
32 | set_all_prime_numbers = set()
33 |
34 | for i in range(2, 10000): # 주어진 범위내의 소수들을 다 저장
35 | if is_prime_number(i):
36 | all_prime_numbers.append(i)
37 | set_all_prime_numbers.add(i)
38 |
39 | n = int(inputs().rstrip())
40 |
41 | for _ in range(n):
42 | num = int(inputs().rstrip())
43 | min_diff = float('inf')
44 |
45 | i = 0
46 | while all_prime_numbers[i] <= (num - all_prime_numbers[i]): # 왼쪽 값이 오른쪽 값보다 무조건 작도록 설정
47 | prime_n = all_prime_numbers[i]
48 | left, right = prime_n, num - prime_n # 좌항, 우항 정의
49 |
50 | if right in set_all_prime_numbers: # 우항도 소수를 만족한다면,
51 | diff = right - left
52 | if diff <= min_diff: # 가장 거리가 작은 답을 출력
53 | min_diff = diff
54 | answer = (left, right)
55 |
56 | i += 1
57 |
58 | print(answer[0], answer[1])
--------------------------------------------------------------------------------
/BOJ/골드바흐의 추측/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-14 16:32
3 |
4 | #### 풀이 방법
5 | - 에라토스테네스의 체를 활용해 10000 이하의 소수를 모두 구해 놓기
6 | - 두 소수의 차이가 가장 작은 골드바흐 파티션 찾기
7 |
8 | 풀이 완료 : 2023-09-14 16:58 (풀이 시간 : 26분)
9 | """
10 |
11 | import sys
12 | from typing import Set
13 |
14 | input = sys.stdin.readline
15 |
16 |
17 | def get_prime_numbers() -> Set[int]:
18 | """
19 | 에라토스테네스의 체 알고리즘을 활용해 10000이하의 모든 소수를 반환한다.
20 | """
21 |
22 | is_primes = [False, False] + [True for _ in range(9999)]
23 | for i in range(2, 101):
24 | j = 2
25 | while i * j <= 10000:
26 | is_primes[i * j] = False # 여기서 나오는 배수는 소수가 아님
27 | j += 1
28 |
29 | # 소수인 인덱스만 반환
30 | return {i for i, is_prime in enumerate(is_primes) if is_prime}
31 |
32 |
33 | prime_numbers = get_prime_numbers()
34 |
35 |
36 | T = int(input())
37 | for _ in range(T):
38 | n = int(input().rstrip())
39 |
40 | i = 2
41 | # 골드바흐 파티션 구하기
42 | while n - i >= i:
43 | # 차이가 가장 작은 골드바흐 파티션을 구하기 위함
44 | if (i in prime_numbers) and (n - i in prime_numbers):
45 | last = i
46 | i += 1
47 |
48 | print(last, n - last)
49 |
--------------------------------------------------------------------------------
/BOJ/공유기 설치/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-24 13:14
3 |
4 | #### 제한 사항
5 | - x_i <= 1,000,000,000 이므로, 가능한 좌표를 완전 탐색하는 방법으로는 x
6 | - 2 <= N <= 200,000 이므로 집을 순회하며 O(NlogN) 알고리즘을 설계해야 한다.
7 | - 집들의 좌표는 정렬되어 주어지지 않음에 주의
8 |
9 | #### 풀이
10 | - 간격을 interval로 설정했을 때 C개의 공유기를 모두 설치할 수 있는지 여부를 판단
11 | - 설정 가능한 간격 중 최소는 1, 최대는 첫 번째 집과 마지막 집의 간격
12 | - 이분탐색을 위해 `low, high = 1, 첫 번째 집과 마지막 집의 간격` 으로 설정
13 | - 집의 좌표가 정렬되어 주어지지 않으므로 정렬이 필요하다.
14 | - mid를 interval로 설정하여 공유기를 설치할 수 있는지 여부를 판단
15 | - 결정 문제 -> 최적화 문제 : 파라메트릭 서치
16 |
17 | #### 시간 복잡도
18 | - 정렬 : O(NlogN)
19 | - 이분탐색 + 설치 여부 판단 = O(logN) * O(N) = O(NlogN)
20 |
21 | 풀이 완료 : 2023-09-24 13:39 (풀이 시간 : 25분 소요)
22 |
23 |
24 | 메모리 | 40548KB
25 | -- | --
26 | 시간 | 272ms
27 |
28 |
29 | """
30 |
31 | import sys
32 |
33 | input = sys.stdin.readline
34 |
35 |
36 | def can_install(interval: int) -> bool:
37 | """
38 | 설치 시 인접한 공유기의 간격을 interval로 설정했을 때 C개의 공유기를 설치할 수 있을지 여부를 반환한다.
39 | """
40 | x = houses[0]
41 | num_wifi = C - 1
42 | for house_x in houses[1:]:
43 | if house_x - x >= interval: # 마지막으로 설치된 공유기 위치와 다음 공유기 위치의 간격이
44 | num_wifi -= 1 # interval보다 짧으면 공유기 설치
45 | x = house_x # 마지막 공유기 위치 업데이트
46 |
47 | if num_wifi == 0: # C개의 공유기 설치 성공
48 | return True
49 |
50 | return False
51 |
52 |
53 | N, C = map(int, input().rstrip().split())
54 | houses = sorted([int(input().rstrip()) for _ in range(N)])
55 |
56 | low, high = 1, houses[-1] - houses[0]
57 |
58 | while low <= high:
59 | mid = (low + high) // 2
60 |
61 | if can_install(mid): # interval을 mid로 설정했을 때 C개를 설치할 수 있으면
62 | low = mid + 1 # 간격을 더 넓혀서 탐색해보기
63 | else: # 설치하지 못하면
64 | high = mid - 1 # 간격을 더 좁혀서 탐색해보기
65 |
66 | print(high) # 설치 가능한 최대 간격은 high에 담겨있음
67 |
--------------------------------------------------------------------------------
/BOJ/공유기 설치/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 25분
3 |
4 |
5 | n: 집의 개수(2 ≤ N ≤ 200,000)
6 | c: 공유기의 개수(2 ≤ C ≤ N)
7 | 집의 좌표 xi (0 ≤ xi ≤ 1,000,000,000)가 주어짐
8 |
9 |
10 | 1. 가장 인접한 두 공유기 사이의 거리를 이분탐색한다.
11 | 2. 가장 인접한 집 간의 거리를 유지하면서 최대한 많은 공유기를 설치했을 때, C개 이상 설치할 수 있으면 정답의 후보가 된다.
12 | - 가능한 경우면, 거리를 더 크게 -> 더 적은 공유기 설치 가능
13 | - 불가능한 경우면, 거리를 더 작게 -> 더 많은 공유기 설치 가능
14 |
15 | <시간복잡도>
16 | O(nlogn)
17 | """
18 | import sys
19 | input = sys.stdin.readline
20 |
21 | # 집, 공유기의 개수
22 | n, c = map(int, input().split())
23 | # 집 좌표
24 | homes = [int(input()) for _ in range(n)]
25 | homes.sort() # 가장 인접한 집을 확인하기 위해 정렬
26 |
27 |
28 | def possible(diff: int) -> bool:
29 | """
30 | diff: 가장 인접한 두 공유기 사이의 거리
31 | """
32 | cnt = 1 # 첫 번째 집부터 설치
33 | last = homes[0]
34 | for home in homes: # 인접한 집들 확인
35 | if home - last >= diff: # 가장 인접한 두 공유기 사이의 거리가 diff 이상이면
36 | cnt += 1 # 공유기 설치 개수 업데이트
37 | last = home # 현재 집 좌표로 갱신
38 | return cnt >= c # 최대한 많은 공유기를 설치했을 때의 개수가 c개 이상이라면 true
39 |
40 | ans = 1
41 | left = 1
42 | right = homes[-1] - homes[0] # 가능한 가장 먼 거리
43 |
44 | while left <= right:
45 | mid = (left+right) // 2 # 가장 인접한 두 공유기 사이의 거리를 이분탐색
46 | if possible(mid): # c개의 공유기가 설치 가능하다면 ans 값 업데이트
47 | ans = mid
48 | left = mid + 1
49 | else: # 설치가 불가능하면 오른쪽 포인터를 이동시켜 인접한 두 공유기 사이의 거리를 줄이고 다시 탐색
50 | right = mid - 1
51 | print(ans)
--------------------------------------------------------------------------------
/BOJ/공유기 설치/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 공유기 설치
3 | https://www.acmicpc.net/problem/2110
4 |
5 | 풀이시간
6 | 00 : 25 ~ 01 : 20 (55분)
7 |
8 | 문제 조건
9 | 2 <= N <= 200,000
10 | 2 <= C <= N
11 | 0 <= x <= 1,000,000,000
12 |
13 | 시간 복잡도 :
14 | O(NlogN + logx * N)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 이진 탐색
18 |
19 | 절대 이진 탐색을 떠올릴 수 없는 문제 설명이지만 과거 풀었던 기억을 통해서 문제를 풀이하였습니다.
20 | (일단 문제 설명이 너무 이상합니다. 가장 인접한 두 공유기 사이의 거리를 최대로... 화가 납니다. 내 자소서가 나을듯)
21 |
22 | 1,000,000,000(10억)이 탐색해야할 영역이기 때문에 logN으로 풀이를 해야하고 이진 탐색을 사용하여 최단 거리를 찾아낼 수 있습니다.
23 | """
24 | import sys
25 |
26 | input = sys.stdin.readline
27 |
28 | # 거리 이진탐색
29 | def binary_search(start, end, houses):
30 | minmum_max_distance = 0
31 |
32 | while start <= end:
33 | mid = (end + start) // 2
34 |
35 | prev_house = houses[0]
36 | count = 1
37 |
38 | # 설정된 mid 값이라면 몇 개의 공유기를 설치 할 수 있는지 확인
39 | for i in range(1, N):
40 | if houses[i] - prev_house >= mid:
41 | count += 1
42 | prev_house = houses[i]
43 |
44 | # 만약 설치된 공유기의 갯수가 C 보다 적다면
45 | if count < C:
46 | # 간격 감소시키기
47 | end = mid - 1
48 | # 만약 설치된 공유기의 갯수가 C 보다 크다면
49 | elif count >= C:
50 | # 간격 증가시키기
51 | start = mid + 1
52 | minmum_max_distance = mid
53 |
54 | return minmum_max_distance
55 |
56 |
57 | N, C = map(int, input().split())
58 |
59 | houses = [int(input()) for _ in range(N)]
60 |
61 | houses.sort()
62 |
63 | print(binary_search(0, houses[-1] - houses[0], houses))
--------------------------------------------------------------------------------
/BOJ/괄호의 값/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 40분
5 |
6 | 접근법
7 | - 문자열 길이 되게 짧음 -> 시간복잡도 신경쓰지말고 꼼꼼히 구현해보자
8 | - 이전 풀이 문제(쇠 막대기)에서 스택 풀이를 제대로 이해못했지만, 비슷한 느낌인 것 같아 쇠 막대기 풀이 참고
9 | - 올바르지 못한 괄호열은 어떻게 정의할까?
10 | - ( 다음 ] 가 오면 땡
11 | - [ 다음 ) 가 오면 땡
12 | - 소괄호와 대괄호의 개수가 다르면 땡
13 |
14 | 회고
15 | - 스택 풀이 이해하기!
16 |
17 | """
18 |
19 | import sys
20 |
21 | text = sys.stdin.readline().strip()
22 | answer = 0
23 | stack = []
24 | tmp_ans = 1 # 곱하기 연산을 해야하므로 1부터 시작
25 |
26 | def filter_correct(text):
27 | cond1 = (text.count('(')==text.count(')')) & (text.count('[')==text.count(']'))
28 | cond2 = (text.count('[)') + text.count('(]') == 0)
29 |
30 | return cond1 & cond2
31 |
32 | if filter_correct(text):
33 | for i in range(len(text)):
34 | if text[i]=="(":
35 | stack.append("(")
36 | tmp_ans *= 2
37 |
38 | elif text[i]=="[":
39 | stack.append("[")
40 | tmp_ans *= 3
41 |
42 | elif text[i]==")":
43 | if text[i-1]=="(":
44 | answer += tmp_ans # 괄호연산이 끝났으므로 정답 더해주기
45 | stack.pop()
46 | tmp_ans //= 2 # 소괄호가 닫혔으므로 곱했던 2를 다시 나눠줌
47 |
48 | elif text[i]=="]":
49 | if text[i-1]=="[":
50 | answer += tmp_ans # 괄호연산이 끝났으므로 정답 더해주기
51 | stack.pop()
52 | tmp_ans //= 3 # 대괄호가 닫혔으므로 곱했던 3을 다시 나눠줌
53 |
54 | else:
55 | answer = 0
56 |
57 | print(answer)
--------------------------------------------------------------------------------
/BOJ/괄호의 값/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 30분
3 |
4 |
5 | - 1 <= 문자열의 길이 <= 30
6 |
7 |
8 | - 단순 구현 문제, 다만 예외처리를 잘해줘야 됨!!
9 | - 괄호값을 모두 더해줘야 하기 때문에 올바른 괄호 쌍이 닫힐 때마다 더해준다.
10 | - 여는 괄호의 경우 항상 stack에 추가해주고, 곱해질 값을 저장해 둘 num 변수를 둔다.
11 | - (의 경우 괄호 안의 값은 모두 *2가 되기 때문에 num *= 2를
12 | - [의 경우 괄호 안의 값은 모두 *3이 되기 때문에 num *= 3를 해준다.
13 | - 닫는 괄호의 경우 올바르지 못한 괄호쌍이 되거나, 올바른 괄호쌍이 되거나 두 가지 경우가 있다.
14 | 1) 올바르지 못한 괄호쌍
15 | - stack이 비어있거나 stack의 top이 자신의 짝 괄호가 아닌 경우
16 | 2) 올바른 괄호쌍
17 | - 직전 괄호가 자신의 짝 괄호인 경우 -> ans 값 갱신
18 | - 모든 괄호값을 순회한 후 stack이 비어있으면 ans값 출력, stack이 비어있지 않다면 0 출력
19 | - ex) [[]
20 |
21 | <시간 복잡도>
22 | O(N)
23 | """
24 | a = input()
25 |
26 | stack = []
27 | ans = 0 # 누적해서 더해질 값
28 | num = 1 # 곱해질 값
29 |
30 | for i in range(len(a)):
31 | if a[i] == '(': # 소괄호
32 | num *= 2
33 | stack.append(a[i])
34 | elif a[i] == '[':
35 | num *= 3 # 대괄호
36 | stack.append(a[i])
37 | elif a[i] == ')':
38 | if not stack or stack[-1] != '(':
39 | print(0)
40 | exit()
41 | if a[i-1] == '(':
42 | ans += num # 직전 괄호가 여는 괄호였다면 값 추가
43 | num //= 2
44 | stack.pop()
45 | elif a[i] == ']':
46 | if not stack or stack[-1] != '[':
47 | print(0)
48 | exit()
49 | if a[i-1] == '[':
50 | ans += num
51 | num //= 3
52 | stack.pop()
53 |
54 | print(ans if not stack else 0)
--------------------------------------------------------------------------------
/BOJ/덱/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 5분
5 |
6 | 접근법
7 | - deque 자료구조로 모두 풀이 가능
8 | - 최대한 간단하게 쓰고 싶어 이항 연산자 사용
9 |
10 | 회고
11 | - 덱 문제 계속 나오니까 뭔가 계속 똑같은 문제를 푸는 느낌이네요 ... ㅎ
12 |
13 | """
14 |
15 | import sys
16 | from collections import deque
17 |
18 | question = sys.stdin.readline # 문자열 입력받음
19 |
20 | N = int(question()) # 반복 횟수 N 입력받음
21 | queue = deque() # 정수를 저장하는 빈 큐 정의
22 |
23 | for _ in range(N):
24 | command = question().rstrip() # 명령 입력받음
25 |
26 | if len(command.split())==2: # push 관련 명령일 경우 숫자도 함께 들어오므로 분리
27 | command, num = command.split()
28 | num = int(num)
29 |
30 | if command=='push_front':
31 | queue.appendleft(num)
32 |
33 | elif command=='push_back':
34 | queue.append(num)
35 |
36 | elif command=='pop_front':
37 | print(queue.popleft() if len(queue)!=0 else -1)
38 |
39 | elif command=='pop_back':
40 | print(queue.pop() if len(queue)!=0 else -1)
41 |
42 | elif command=='size':
43 | print(len(queue))
44 |
45 | elif command=='empty':
46 | print(1 if len(queue)==0 else 0)
47 |
48 | elif command=='front':
49 | print(queue[0] if len(queue)!=0 else -1)
50 |
51 | elif command=='back':
52 | print(queue[-1] if len(queue)!=0 else -1)
--------------------------------------------------------------------------------
/BOJ/덱/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 6분
3 |
4 |
5 | 주어진 대로 구현하면 된다.
6 | 문제의 이름처럼 deque 라이브러리를 사용해서 구현하면 앞, 뒤에 숫자를 넣고 빼는 작업을 O(1)에 할 수 있다.
7 |
8 | 시간복잡도: o(n)
9 | """
10 |
11 | import sys
12 | input = sys.stdin.readline
13 | from collections import deque
14 |
15 |
16 | q = deque()
17 | n = int(input()) # 명령의 수 입력받기
18 | for _ in range(n): # 명령 입력받기
19 | op, *x = input().split()
20 | if op == 'push_front':
21 | q.appendleft(x[0])
22 | elif op == 'push_back':
23 | q.append(x[0])
24 | elif op == 'pop_front':
25 | print(q.popleft() if q else -1)
26 | elif op == 'pop_back':
27 | print(q.pop() if q else -1)
28 | elif op == 'size':
29 | print(len(q))
30 | elif op == 'empty':
31 | print(1 if not q else 0)
32 | elif op == 'front':
33 | print(q[0] if q else -1)
34 | else: # back
35 | print(q[-1] if q else -1)
36 |
--------------------------------------------------------------------------------
/BOJ/덱/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 덱
3 | https://www.acmicpc.net/problem/10866
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 덱을 사용한 구현
7 |
8 | 고찰
9 | python collections의 deque을 사용하여 각 명령어와 내장 메소드를 매핑해줌으로써 해결 할 수 있었습니다.
10 | input 데이터를 입력 받을 때 '변수, *변수 = input().split()'을 사용한다면 커맨드와 인자를 받는 경우를 구분 할 수 있어 좋은 것 같습니다.
11 |
12 | """
13 | import sys
14 | from collections import deque
15 |
16 | input = sys.stdin.readline
17 |
18 | # 입력받을 명령어 갯수
19 | N = int(input())
20 |
21 | # 업데이트 할 덱 구현
22 | D = deque()
23 |
24 |
25 | for idx in range(N):
26 | cmd, *args = input().split()
27 | # 각 케이스에 맞는 메소드 매핑 후 모든 예외에 대해서는 -1이 반환되어야하기 때문에 else문을 통해 한 번에 예외처리를 진행
28 | if cmd == "push_front":
29 | D.appendleft(*args)
30 | elif cmd == "push_back":
31 | D.append(*args)
32 | elif cmd == "pop_front" and D:
33 | print(D.popleft())
34 | elif cmd == "pop_back" and D:
35 | print(D.pop())
36 | elif cmd == "size":
37 | print(len(D))
38 | elif cmd == "empty":
39 | print(0 if D else 1)
40 | elif cmd == "front" and D:
41 | print(D[0])
42 | elif cmd == "back" and D:
43 | print(D[-1])
44 | else:
45 | print(-1)
46 |
--------------------------------------------------------------------------------
/BOJ/덱/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | [백준 10866번: 덱 (Silver 4)](https://www.acmicpc.net/problem/10866)
3 | - 풀이 시간: 10분
4 | - 접근 방법
5 | - 덱 자료형 사용
6 | - 문제 그대로 구현
7 | """
8 |
9 | from collections import deque
10 | import sys
11 | input = sys.stdin.readline
12 |
13 | deq = deque()
14 | n = int(input())
15 | for i in range(n):
16 | cmd = input().split()
17 | if cmd[0] == 'push_front':
18 | deq.appendleft(cmd[1])
19 | elif cmd[0] == 'push_back':
20 | deq.append(cmd[1])
21 | elif cmd[0] == 'pop_front':
22 | if not deq:
23 | print(-1)
24 | else:
25 | x = deq.popleft()
26 | print(x)
27 | elif cmd[0] == 'pop_back':
28 | if not deq:
29 | print(-1)
30 | else:
31 | x = deq.pop()
32 | print(x)
33 | elif cmd[0] == 'size':
34 | print(len(deq))
35 | elif cmd[0] == 'empty':
36 | if not deq:
37 | print(1)
38 | else:
39 | print(0)
40 | elif cmd[0] == 'front':
41 | if not deq:
42 | print(-1)
43 | else:
44 | print(deq[0])
45 | elif cmd[0] == 'back':
46 | if not deq:
47 | print(-1)
48 | else:
49 | print(deq[-1])
50 |
--------------------------------------------------------------------------------
/BOJ/문자열 지옥에 빠진 호석/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - BFS 로 한번 탐색해보자
8 | - 각 선호 문자열마다 BFS 를 돌리면 너무 오래 걸림
9 | - 한번의 bfs 에 문자열 배열을 저장해놓고 선호 문자열이 존재하는지 여부를 조사하는 것이 나을듯
10 |
11 | - 답지 풀이
12 | - BFS/DFS 둘 다 사용해도 상관없는듯 함
13 | - 본 풀이에서는 재귀로 구현이 편리한 DFS 활용
14 | - visit 배열이 아닌 딕셔너리를 활용
15 | - key: 문자열, value: 해당 문자열이 나올 수 있는 경우의수
16 | - 조건문이 아닌 나머지 연산을 활용하여 범위를 넘어간 DFS 를 구현
17 |
18 | 회고
19 | - 하 문제 길ㄷㅏ ... 집중력이 많이 부족해서 놓친 부분이 꽤 있었음 (ex: 신이 좋아하는 문자열의 길이는 5이하 조건)
20 |
21 | """
22 |
23 | import sys
24 | sys.setrecursionlimit(987654321)
25 |
26 | inputs = sys.stdin.readline
27 |
28 | n, m, k = map(int, inputs().split())
29 | arr = [list(inputs().rstrip()) for _ in range(n)]
30 | ans, ans_list = {}, [] # 가능한 경우의 수를 담을 dict, 신이 선호하는 문자열 list
31 |
32 | for _ in range(k):
33 | data = inputs().rstrip()
34 | ans[data] = 0
35 | ans_list.append(data)
36 |
37 | directions = [(1,0), (0,1), (-1,0), (0,-1), (1,-1), (1,1), (-1,1), (-1,-1)]
38 |
39 | def DFS(x, y, cnt, string):
40 | if cnt > 5: # 신이 좋아하는 문자열의 길이는 5이하
41 | return
42 |
43 | if string in ans: # 문자열이 딕셔너리에 존재 -> 가능한 경우의 수 +1
44 | ans[string] += 1
45 |
46 | for dx, dy in directions:
47 | nx, ny = (x + n + dx) % n, (y + m + dy) % m # 범위를 넘어가면 다시 돌아오는 조건에 맞게 변환
48 | DFS(nx, ny, cnt + 1, string + arr[nx][ny])
49 |
50 | # 가능한 모든 그래프의 시작점에서 DFS
51 | for i in range(n):
52 | for j in range(m):
53 | start = ''
54 | DFS(i, j, 1, start + arr[i][j])
55 |
56 | for k in ans_list:
57 | if k in ans:
58 | print(ans[k])
--------------------------------------------------------------------------------
/BOJ/문자열 지옥에 빠진 호석/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 30분
3 |
4 |
5 | N행 M열의 격자(각 칸에 알파벳이 써있고, 환형으로 이어진다)
6 | 아무 곳에서 시작해 상하좌우나 대각선 방향의 칸으로 한 칸씩 이동할 수 있다. (이미 지나왔던 칸들을 다시 방문하는 것은 허용)
7 | 시작하는 격자의 알파벳을 시작으로, 이동할 때마다 각 칸에 써진 알파벳을 이어 붙여 문자열을 만든다.
8 | 각 문자열마다 만들 수 있는 경우의 수를 구해라.(방문 순서가 다르면 다른 경우)
9 | ex. (1, 1) -> (1, 2) != (1, 2) -> (1, 1)
10 |
11 | - n, m: 격자의 크기(3 ≤ N, M ≤ 10, 자연수)
12 | - k: 신이 좋아하는 문자열의 개수(1 ≤ K ≤ 1,000, 자연수)
13 | - 1 ≤ 신이 좋아하는 문자열의 길이 ≤ 5
14 |
15 |
16 | n과 m이 매우 작기 때문에 DFS 탐색(아무 곳에서 시작할 수 있으니 모든 좌표를 탐색)
17 | 문자열의 길이는 최대 5이므로 그 이후는 탐색 x
18 | 문자열을 만들 때마다 딕셔너리에 해당 문자열의 경우의 수를 + 1 해준다.
19 |
20 | <시간복잡도>
21 | O(N * M * 8^5)
22 | - dfs 함수는 각 칸에서 재귀적으로 최대 8개의 인접한 칸을 탐색하고, 최대 깊이 5까지 탐색할 수 있음: O(8^5)
23 | - 모든 칸에 대해서 시작할 수 있음: O(NM)
24 | """
25 | import sys
26 | input = sys.stdin.readline
27 | from collections import defaultdict
28 |
29 |
30 | # 상하좌우, 대각선
31 | dx = [-1, 1, 0, 0, -1, -1, 1, 1]
32 | dy = [0, 0, -1, 1, -1, 1, -1, 1]
33 |
34 | cnt = defaultdict(int) # 각 문자열이 등장하는 경우의 수
35 | def dfs(x: int, y: int, s: str) -> None:
36 | # 문자열 s가 등장한 경우의 수를 1추가
37 | cnt[s] += 1
38 | if len(s) == 5: # 문자열의 길이는 최대 5이기 때문에 더 이상 탐색 x
39 | return
40 | for k in range(8):
41 | nx, ny = (x+dx[k]) % n, (y+dy[k]) % m
42 | dfs(nx, ny, s+board[nx][ny])
43 |
44 | # 격자의 크기, 신이 좋아하는 문자열의 개수
45 | n, m, k = map(int, input().split())
46 | board = [input() for _ in range(n)]
47 |
48 | # 모든 칸에 대해서 DFS 수행
49 | for i in range(n):
50 | for j in range(m):
51 | dfs(i, j, board[i][j])
52 |
53 | # 신이 좋아하는 K개의 각 단어를 만들 수 있는 경우의 수 출력
54 | for _ in range(k):
55 | word = input().rstrip()
56 | print(cnt[word])
--------------------------------------------------------------------------------
/BOJ/쇠막대기/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 2시간
5 |
6 | 접근법
7 | - 괄호만으로 어떻게 쇠 막대기가 잘린 것을 표현할 것인가?
8 | - 레이저의 괄호와 쇠막대기의 괄호 표기법이 똑같음 -> "()" 가 쪼개지는 형상으로 표현가능하지 않을까?
9 | - 예시
10 | - (()) -> ()()
11 | - (()()) -> ()()()
12 | - 즉 큰 '()' 안에 들어있는(=막대기) 레이저('()') 의 개수 + 1 하면은 답임
13 | - 위 생각에 근거해서 풀이 -> 답은 제대로 나오는 것 같은데 시간초과
14 | - 문자열을 계속해서 생성해내는 알고리즘 때문에 시간초과가 나는 것 같음
15 | - 시간 너무 많이 써서 그냥 답 봄
16 |
17 | 회고
18 | - 스택 자료구조에 대해서 모르고 있었던게 아닌데도 써먹을려고 하니까 어렵다 ㅠ ㅠ
19 | - 풀이 이해를 잘 못하겠서용 ... ㅜㅜ
20 | - 초기풀이가 당연히 시간복잡도가 큰 풀이인데도 불구하고 혹시 모른다는 생각으로 계속 풀어보다가 시간 너무 낭비함
21 | - 저번에도 이런적이 꽤 있는데 의식하고 빨리 다른 풀이로 돌아갈 줄 알아야함 (제발)
22 |
23 | """
24 |
25 | ### 기존 풀이
26 | import sys
27 |
28 | text = sys.stdin.readline().strip()
29 | stop_n = int((int(len(text)/2) + 1)/2) # "()" 로 쪼갰을 때 전부 "()()()()..." 가 되면 다 쪼개진거 -> "()()()..." 의 개수
30 | ans = 0
31 | n = 1
32 |
33 | while text != '()'* stop_n: # 다 쪼개지면 멈춤
34 | cnt = 0 # 쪼개진 막대기 개수를 카운트하는 변수
35 | while text != text.replace("(" + "()"*n + ")", "()"*n, 1): # 더 이상 안 쪼개질 때 까지 (대부분 한 번만에 쪼개짐)
36 | cnt += text.count("(" + "()"*n + ")") # 쪼개지는 막대 개수
37 | text = text.replace("(" + "()"*n + ")", "()"*n) # 실제로 쪼개기
38 | ans += (n+1) * (cnt) # 쪼갠 막대 개수 * (쪼개진 막대기 개수)
39 | n += 1
40 |
41 | print(ans)
42 |
43 |
44 | ### 스택 활용 풀이
45 | import sys
46 |
47 | text = sys.stdin.readline().strip()
48 | answer = 0
49 | stack = []
50 |
51 | for i in range(len(text)):
52 | if text[i] == '(': # 스택 쌓기
53 | stack.append('(')
54 |
55 | else:
56 | if text[i-1] == '(': # ()라면 (를 pop하고 현재 스택에 들어있는 ( 수만큼 값을 더해준다.
57 | stack.pop()
58 | answer += len(stack)
59 |
60 | else:
61 | stack.pop()
62 | answer += 1 # 끄트머리 막대기 부분을 더해준다
63 |
64 | print(answer)
--------------------------------------------------------------------------------
/BOJ/쇠막대기/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023.08.11 23:31
3 |
4 | - 인접한 괄호 쌍 '()' 을 제외하고 떨어져 있는 괄호 쌍은 모두 쇠 막대기를 나타낸다.
5 | - 괄호 문자의 개수는 최대 100,000개이므로 O(nlogn)까지는 될 것 같다.
6 | - 여는 괄호 '('가 연속으로 나오는 개수를 세고, 닫는 괄호 ')'가 나왔을 때 이전 괄호가 여는 괄호 '('이면(레이저), 직전까지 여는 괄호의 수 만큼 막대 조각을 얻는 데서 아이디어를 얻어 풀이 시작
7 | - 단 한번의 순회로 답을 얻어낼 수 있으므로 O(N)의 풀이가 가능할 것이다.
8 |
9 | 풀이 완료 : 2023.08.12 00:23
10 | '''
11 |
12 | import sys
13 |
14 | input = sys.stdin.readline
15 |
16 | def solution() -> None:
17 | brakets: str = input().rstrip() # 그냥 rstrip()을 생활화할께요 이거 때문에 시간을 ㅠㅠㅠ
18 | count: int = 0 # 중첩 여는 괄호 수를 관리할 변수
19 | answer: int = 0 # 잘린 막대 수를 관리할 변수
20 |
21 | for idx in range(len(brakets)): # 모든 괄호를 순회
22 | if brakets[idx] == '(': # 여는 괄호가 나오면
23 | count += 1 # 여는 괄호 중첩 개수 +1
24 | else: # 닫는 괄호가 나오면
25 | if brakets[idx-1] == '(': # 직전 괄호가 여는 괄호일 경우 (레이저)
26 | count -= 1 # 직전 여는 괄호는 중첩 제외
27 | answer += count # 현재 열린 괄호 수 만큼의 막대 조각을 얻음
28 | else: # 직전 괄호가 닫는 괄호일 경우
29 | count -= 1 # 열린 괄호 중 가장 짧은 괄호가 닫힘, 중첩 열린 괄호 수 -1
30 | answer += 1 # 직전 레이저에 의해 남은 부분의 막대 조각을 고려해주어야 함
31 |
32 | print(answer)
33 |
34 | solution()
--------------------------------------------------------------------------------
/BOJ/쇠막대기/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 25분
3 |
4 |
5 | 괄호 문자의 개수는 최대 100,000개 -> O(NlogN)내로 해결해야 함
6 |
7 |
8 | - 레이저와 쇠막대기를 구분해줘야 한다.
9 | 1) 여는 괄호의 경우 -> stack에 현재 인덱스+1을 추가
10 | 2) 닫는 괄호의 경우(레이저 or 쇠막대기인지 구분 가능)
11 | - 레이저는 stack의 top과 인덱스가 1차이가 남
12 | - 쇠막대기의 끝은 stack의 top과 항상 1보다 더 많은 차이가 남
13 |
14 | <시간복잡도>
15 | - O(n)
16 | """
17 |
18 | a = input()
19 | stack = []
20 | ans = 0 # 잘려진 쇠막대기 조각의 총 개수
21 | cnt = 0 # stack에 들어있는 막대기(열린괄호)의 개수
22 |
23 | for i in range(len(a)):
24 | if a[i] == '(': # 열린 괄호는 스택에 추가
25 | stack.append(i+1)
26 | cnt += 1
27 | else: # 닫힌 괄호이면 레이저인지 쇠막대기의 끝인지 구분해야됨
28 | cnt -= 1
29 | if stack[-1] == i: # 레이저는 인덱스가 1차이 남
30 | # 레이저면 잘려진 막대기의 개수를 더해줌(잘려진 막대기의 개수 = 스택의 길이)
31 | stack.pop()
32 | ans += cnt
33 | else: # 쇠막대기의 끝이면 + 1
34 | stack.pop()
35 | ans += 1
36 | print(ans)
--------------------------------------------------------------------------------
/BOJ/수 이어 쓰기 2/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-14 19:39
3 |
4 | 숫자 길이 -> 숫자 개수
5 | 1 -> 9 9
6 | 2 -> 90 180
7 | 3 -> 900 2700
8 | 4 -> 9000 36000
9 | 5 -> 90000 450000
10 | ... ...
11 |
12 | - 이를 배열에 담고 누적합을 구하면 1부터 i자리 수 끝까지 수의 길이를 알 수 있다.
13 | - ex) 1부터 2자리 끝 수까지의 길이 : 9 + 180 = 189
14 | - 바로 직전 자리수 끝까지 수의 길이를 K에서 빼준다.
15 | - ex) K가 23일 때 직전 자리 수(1자리 수)의 길이인 9를 빼면 14
16 | - (K-1)을 직전 자리수 +1(현재 자리 수)로 나눠준 몫은 현재 자리 수에서 몇 번째 수까지 구해야 하는지에 해당된다.
17 | - K-1(13) // 2(직전 자리 수 +1) = 6
18 | - 두번째 자리수에서 6번쨰 수까지 => 10 11 12 13 14 15 16 까지 구해야 한다.
19 | - 이 때 이 수가 N 보다 작으면 -1 출력
20 | - (K-1)을 직전 자리수 +1(현재 자리 수)로 나눈 나머지는 마지막 구한 수의 자리 수에 해당된다.
21 | - 마지막 구한 수 : 16
22 | - K-1(13) % 2(직전 자리 수 +1) = 1
23 | - "16"[1] = 6
24 |
25 | 너무 어거지로 정답을 이끌어낸 느낌인데, 다른 풀이도 이 풀이와 다른 점은 없었음
26 | i자리 수를 끝까지 이어붙였을 때 길이를 구해 활용하는 게 핵심인 것 같다.
27 |
28 |
29 | 풀이 완료 : 2023-09-14 20:59 (풀이 시간 : 1시간 20분)
30 | """
31 |
32 | N, K = map(int, input().split())
33 |
34 | mem = [0] + [9 * (i + 1) * (10**i) for i in range(9)]
35 | for i in range(1, len(mem)): # 개수 누적 합 구하기
36 | mem[i] += mem[i - 1]
37 |
38 | for idx, num_length in enumerate(mem):
39 | if num_length >= K:
40 | cut = idx - 1 # 직전 자리수
41 | break
42 |
43 | K -= mem[cut] # 이전 자리수까지 수 개수 빼기
44 |
45 | m, r = divmod((K - 1), cut + 1) # 16, 20 line 참고
46 | answer_num = 10 ** (cut) + m
47 |
48 | print(str(answer_num)[r] if N >= answer_num else -1)
49 |
--------------------------------------------------------------------------------
/BOJ/수 이어 쓰기 2/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 35분
3 |
4 |
5 | N : 1 ≤ N ≤ 100,000,000
6 | K : 1 ≤ k ≤ 1,000,000,000
7 |
8 |
9 | 수를 다 진짜로 이어서 붙이면 시간초과 날 수 밖에 없음
10 | 어떻게 풀 것인가?
11 |
12 | 1 ~ 9 | 10 ~ 99 | 100 ~ 999 | ...
13 | 각각 구간별로 수가 갖는 길이가 1, 2, 3으로 증가하는 것을 알 수 있다
14 | k가 어느 길이 구간에 속하는지 찾아야 한다.(글로 설명을 포기..)
15 |
16 | <시간 복잡도>
17 | k가 10억이라고 해도, while문은 최대 8번밖에 안 돌게 됨 -> 상수시간 해결
18 | """
19 | n, k = map(int, input().split())
20 |
21 | ans = 0 # k번재 자리 숫자를 저장하는 변수
22 | length = 1 # 현재 자리 숫자의 길이
23 | num = 9 # 현재 자리 숫자의 범위(1자리 숫자: 9, 2자리 숫자:90, 3자리 숫자: 900)
24 |
25 | while k - (length * num) > 0: # k가 속하는 구간 찾기
26 | k -= length * num # 현재 자리 숫자 범위를 넘어갔을 때 k를 조정
27 | ans += num
28 | length += 1 # 다음 구간으로 넘어가기
29 | num *= 10 # 다음 자리 구간의 범위
30 |
31 | q, r = divmod(k, length)
32 | ans += 1
33 |
34 | if r == 0:
35 | ans = ans + (q-1)
36 | else:
37 | ans = ans + q
38 |
39 | # 수의 길이가 k보다 작아서 k번째 자리 숫자가 없는 경우는 -1
40 | print(str(ans)[r-1] if ans <= n else -1)
--------------------------------------------------------------------------------
/BOJ/스택 수열/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 30분
3 |
4 |
5 | n : 1 ≤ n ≤ 100,000
6 | -> 최대 O(nlogn)으로 설계 가능
7 |
8 |
9 | 1. 주어진 수열의 수를 순서대로 하나씩 확인한다.
10 | 2. 확인하고 있는 수와 지금까지 스택에 넣은 가장 큰 수를 비교한다.
11 | 2-1) 확인하고 있는 수가 현재까지 스택에 넣은 가장 큰 수보다 큰 경우
12 | - 수열에 '지금까지 넣은 가장 큰 수+1'부터 '현재 확인하고 있는 수'까지 스택에 push한다. ans에 +를 추가한다.
13 | - 스택의 top(현재 확인하고 있는 수와 동일)을 pop하고, ans에 -를 추가한다.
14 | 2-2) 확인하고 있는 수가 현재까지 스택에 넣은 가장 큰 수보다 작거나 같은 경우
15 | - 스택의 top이 현재 확인하고 있는 수와 같다면, top을 pop하고 ans에 -를 추가한다.
16 | - 스택의 top이 현재 확인하고 있는 수와 같지 않다면, 더 이상 수열을 만들 수 없으므로 'NO'를 출력하고 종료한다. <- '스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자' 규칙 때문에
17 | 3. 최종적으로 만들어진 연산을 한 줄에 한 개씩 출력한다.
18 | """
19 |
20 | import sys
21 | input = sys.stdin.readline
22 |
23 | n = int(input()) # 1 ≤ n ≤ 100,000
24 | sequence = [int(input()) for _ in range(n)] # 1~n의 정수로 이루어진 수열(같은 정수가 두 번 나오지 않음)
25 |
26 | max_num = 0 # 현재까지 stack에 추가한 가장 큰 수
27 | ans = []
28 | stack = []
29 | for x in sequence: # 주어진 수열의 수를 순서대로 하나씩 확인
30 | if x > max_num: # 현재 확인하고 있는 수가 지금까지 나온 수보다 큰 경우
31 | while x > max_num: # max_num+1부터 x까지 stack에 push
32 | max_num += 1
33 | stack.append(max_num)
34 | ans.append('+')
35 | stack.pop() # stack의 top에 있는 x를 pop
36 | ans.append('-')
37 | else: # 확인하고 있는 수가 스택에 넣은 가장 큰 수보다 작거나 같은 경우
38 | if stack[-1] == x: # stack의 top이 현재 확인하고 있는 수와 같다면
39 | stack.pop() # pop
40 | ans.append('-')
41 | else: # 현재 stack의 top이 x와 같지 않다면, 더 이상 주어진 수열을 만들 수 없음
42 | print('NO') # NO를 출력하고
43 | sys.exit(0) # 종료
44 |
45 | print('\n'.join(map(str, ans)))
46 |
--------------------------------------------------------------------------------
/BOJ/싸이버개강총회/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 15분
5 |
6 | 접근법
7 | - 채팅 기록은 10만 이하 -> O(NlogN)
8 | - HH:MM 단위를 분 단위로 변환하면 편리할듯함
9 | - 채팅 기록이 몇 개인지 입력으로 안 주어진다..?
10 | - try except 으로 수동 처리
11 | - 시간은 정렬되어 입력되므로, 퇴근 시 출근 기록이 있는지 확인해야함
12 | - 즉 in, not in 연산 필요 -> set 자료형 사용하여 시간복잡도 개선
13 |
14 | 회고
15 | - set 을 자주 활용하다보니 어려움없이 풀어낼 수 있었음!
16 |
17 | """
18 |
19 | import sys
20 |
21 | inputs = sys.stdin.readline
22 |
23 | def hour_to_minute(x): # 분 단위로 변환
24 | return int(x[:2])*60 + int(x[3:])
25 |
26 | S, E, Q = map(hour_to_minute, inputs().split())
27 | answer = 0
28 | check = set()
29 |
30 | while True:
31 | try:
32 | time, name = inputs().split()
33 | time = hour_to_minute(time)
34 |
35 | if time <= S: # 출근 찍어진다면
36 | check.add(name) # 출석부에 추가
37 |
38 | elif E <= time <= Q: # 퇴근 찍어진다면
39 | if name in check: # 출석도 한 친구라면
40 | answer += 1 # 정답 +1
41 | check.discard(name) # 퇴근 가능 시간대에 여러 번 채팅친 경우를 방지
42 | except:
43 | break
44 |
45 | print(answer)
--------------------------------------------------------------------------------
/BOJ/싸이버개강총회/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-28 16:20
3 |
4 | #### 풀이
5 | - 스트리밍 시작 -> 개강총회 시작 -> 개강총회 끝 -> 스트리밍 끝
6 | - 스트리밍 시작 -> 개총(S) : 개강총회 시작 정시까지 채팅 기록 체크
7 | - 개강총회 끝(E) -> 스트리밍 끝(Q) : 채팅 기록 체크
8 | - 시간에 대한 문자열을 모두 datetime.datetime.strptime() 으로 파싱
9 | - 시간에 대한 대소 비교를 수행하며
10 | - 개총 이전 시간의 기록 : set에다가 이름 기록
11 | - 개총 ~ 개총끝 : 패스
12 | - 개총끝 ~ 스트리밍 끝 : 기록된 이름이 있는 경우 카운트
13 | - 이때 중복해서 채팅 치는 경우를 고려해 또 다른 set에다 넣어서 개수를 센다.
14 | - 시간에 대한 대소 비교 수행 시 부등호(이상, 초과) 조심해야 함!
15 |
16 | 풀이 완료 : 2023-09-28 17:10 (풀이 시간 : 50분 소요)
17 |
18 | 메모리 | 시간
19 | -- | --
20 | 59568KB | 824ms
21 |
22 | """
23 |
24 | import sys
25 | import datetime
26 |
27 | input = sys.stdin.readline
28 |
29 | S, E, Q = map(
30 | lambda x: datetime.datetime.strptime(x, "%H:%M").time(), input().rstrip().split()
31 | ) # datetime time객체 parsing
32 |
33 | student_in = set() # 출석 가능한 입장 기록
34 | student_out = set() # 출석 가능한 퇴장 기록
35 |
36 | records = sys.stdin.readlines()
37 | # records = [] # IDE에서 테스트 용
38 | # while True:
39 | # s = input().rstrip()
40 |
41 | # if s == "!":
42 | # break
43 |
44 | # records.append(s)
45 |
46 | idx = 0
47 | count = 0
48 |
49 | while idx < len(records):
50 | t, name = records[idx].split()
51 | # datetime time객체 parsing
52 | t = datetime.datetime.strptime(records[idx][:5], "%H:%M").time()
53 | if t <= S: # 부등호 조심! 개총 시작하자마자 채팅 친 경우도 출석 인정
54 | student_in.add(name)
55 | elif t < E: # 부등호 조심! 개총 끝나자마자 채팅 친 경우도 출석 인정
56 | pass
57 | elif t <= Q:
58 | if name in student_in:
59 | student_out.add(name) # 중복 채팅 고려
60 |
61 | idx += 1
62 |
63 | print(len(student_out))
64 |
--------------------------------------------------------------------------------
/BOJ/싸이버개강총회/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 |
5 | - s: 개강총회 시작 시간
6 | - e: 개강총회를 끝낸 시간
7 | - q: 스트리밍을 끝낸 시간
8 | (00:00 ≤ S < E < Q ≤ 23:59)
9 |
10 | 최대 10만개의 채팅 기록이 주어진다.
11 |
12 |
13 | 문제에 주어진대로
14 | 1) 개강총회를 시작하기 전, 입장 확인
15 | 2) 개강총회를 끝내고 나서 스트리밍을 끝날 때, 퇴장 확인
16 |
17 | 두 경우를 set자료형을 통해 확인한다.
18 | 채팅 기록이 시간 순서대로 주어지기 때문에 입장 확인이 됐다면 set에 추가하고, 입장확인된 학회원이 제대로 퇴장했다면 해당 이름을 제거한다.
19 |
20 | <시간복잡도>
21 | O(채팅 기록의 수): 최대 10만
22 | """
23 | import sys
24 | input = sys.stdin.readline
25 |
26 |
27 | attend = set() # 출석 여부를 확인하기 위한 set
28 | ans = 0 # 출석이 확인된 학회원의 인원수
29 |
30 | # 개강총회 시작 시간, 끝낸 시간, 스트리밍 끝낸 시간
31 | s, e, q = input().split()
32 | while True:
33 | try:
34 | time, nickname = input().split()
35 | # 1) 입장 여부 확인
36 | if time <= s:
37 | attend.add(nickname)
38 | # 2) 퇴장 여부 확인
39 | elif e <= time <= q and nickname in attend:
40 | attend.remove(nickname)
41 | ans += 1
42 | except:
43 | break
44 | print(ans)
--------------------------------------------------------------------------------
/BOJ/싸이버개강총회/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 싸이버개강총회
3 | https://www.acmicpc.net/problem/19583
4 |
5 | 풀이시간
6 | 11:20 ~ 12:10 (50분)
7 |
8 | 문제 조건
9 | (00:00 ≤ S < E < Q ≤ 23:59)
10 | 0 < 채팅 기록 < 10만
11 |
12 | 시간 복잡도 :
13 | strptime은 N 길이의 문자열의 길이를 파싱할 때 O(N) - O(5)
14 | O(100,000)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 해시 테이블
18 |
19 | 시간을 datetime 객체로 변환 후 조건의 시간들과 비교하여
20 | 출석부에 삽입 혹은 제거 연산을 시행하고 그 횟수를 남긴다.
21 | """
22 | import sys
23 | from datetime import datetime
24 |
25 | input = sys.stdin.readline
26 |
27 | S,E,Q = input().split()
28 |
29 | # S,E,Q를 Datatime 객체로 변환
30 | S = datetime.strptime(S, "%H:%M")
31 | E = datetime.strptime(E, "%H:%M")
32 | Q = datetime.strptime(Q, "%H:%M")
33 |
34 | # 출석부 이름을 저장할 집합 생성
35 | name_table = set()
36 |
37 | # 출석 인정된 사람 수
38 | count = 0
39 |
40 | while True:
41 | log = input().split()
42 |
43 | # log가 입력되지 않으면 break
44 | if not log:
45 | break
46 |
47 | time = datetime.strptime(log[0], "%H:%M")
48 |
49 | # 개강총회 시작전에 채팅을 남겼다면 출석부에 이름 추가
50 | if time <= S:
51 | name_table.add(log[1])
52 |
53 | # 개강 총회가 끝나고 스트리밍 종료 전에 채팅을 남겼다면 출석부에서 이름 확인 후 제거 및 출석 인정
54 | elif E <= time <= Q:
55 | if log[1] in name_table:
56 | name_table.remove(log[1])
57 | count += 1
58 |
59 | print(count)
--------------------------------------------------------------------------------
/BOJ/암호 만들기/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 25분
3 |
4 |
5 | - L, C (3 ≤ L ≤ C ≤ 15)
6 |
7 |
8 | - 주어진 알파벳 C개 중 L개를 중복없이 선택
9 | - 만든 암호가 한 개 이상의 모음, 두 개 이상의 자음을 포함하고 있는지 확인
10 |
11 | <시간 복잡도>
12 | O(2^C * L): go 함수에서 각 재귀 호출마다 i가 start부터 c-1까지 변화하면서 두 가지 경우의 수가 발생하므로, 재귀 호출의 총 횟수는 2^C 번 * check 함수는 O(L)
13 | """
14 |
15 | # l: 암호의 길이, c: 알파벳의 개수
16 | l, c = map(int, input().split())
17 | alpha = sorted(input().split()) # c개의 알파벳
18 | pw = [] # 암호를 저장하는 리스트
19 |
20 | # 한 개 이상의 모음, 두 개 이상의 자음이 있는지 확인하는 함수
21 | def check(password: str) -> bool:
22 | ja, mo = 0, 0 # 자음, 모음
23 | for x in password:
24 | if x in 'aeiou':
25 | mo += 1
26 | else:
27 | ja += 1
28 | return ja >= 2 and mo >= 1
29 |
30 | def go(index: int, start: int) -> None:
31 | """
32 | index: 처리하고 있는 암호의 인덱스
33 | start: 다음에 올 암호의 시작 인덱스
34 | """
35 | if index == l: # l개의 알파벳을 모두 선택하면 종료
36 | result = ''.join(pw)
37 | if check(result): # 자음, 모음 조건 충족한다면 출력
38 | print(result)
39 | return
40 |
41 | for i in range(start, c):
42 | pw.append(alpha[i]) # 알파벳을 암호에 추가
43 | go(index + 1, i + 1) # 다음번째에 올 암호처리
44 | pw.pop() # 마지막으로 추가한 알파벳 제거(백트래킹)
45 |
46 | go(0, 0)
--------------------------------------------------------------------------------
/BOJ/에디터/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분 소요
5 |
6 | 접근법
7 | - 리스트로 접근하는 것 보다는 문자열로 접근하는 것이 더 빠를 것이라고 생각했음
8 | - 구현하는데에는 딱히 어려운 점은 없었음
9 | - 하지만 시간초과 오류가 발생 (해당 문제가 시간제한이 조금 빡세다고 함)
10 | - 시간초과 문제를 해결하지 못해서 결국 레퍼런스 참조
11 | - append, pop 의 시간복잡도가 O(1) 인 것을 활용하여 스택 두 개를 사용하는 것이 핵심
12 |
13 | 회고
14 | - 스택 자료구조에 대해서 조금 더 알아보자!
15 |
16 | """
17 |
18 | ### 기존 풀이
19 | import sys
20 |
21 | text = sys.stdin.readline().strip()
22 | n = int(sys.stdin.readline())
23 | cursur_loc = len(text)
24 |
25 | for _ in range(n):
26 | command = sys.stdin.readline().strip()
27 | if (command[0] == 'L') & (cursur_loc != 0):
28 | cursur_loc -= 1
29 | elif (command[0] == 'D') & (cursur_loc != len(text)):
30 | cursur_loc += 1
31 | elif (command[0] == 'B') & (cursur_loc != 0):
32 | text = text[:cursur_loc-1] + text[cursur_loc:]
33 | cursur_loc -= 1
34 | elif (command[0] == 'P'):
35 | text = text[:cursur_loc] + command[2] + text[cursur_loc:]
36 | cursur_loc += 1
37 |
38 | print(text)
39 |
40 |
41 | ### 레퍼런스 참고 풀이
42 | import sys
43 |
44 | left_stack = list(sys.stdin.readline().rstrip())
45 | right_stack = []
46 | n = int(sys.stdin.readline().rstrip())
47 |
48 | for _ in range(n):
49 | command = list(sys.stdin.readline().split())
50 |
51 | if (command[0] == 'L') & (left_stack):
52 | right_stack.append(left_stack.pop())
53 | elif (command[0] == 'D') & (right_stack):
54 | left_stack.append(right_stack.pop())
55 | elif (command[0] == 'B') & (left_stack):
56 | left_stack.pop()
57 | elif (command[0] == 'P'):
58 | left_stack.append(command[1])
59 |
60 | print("".join(left_stack + list(reversed(right_stack))))
--------------------------------------------------------------------------------
/BOJ/에디터/jisu.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from collections import deque
3 |
4 |
5 | input = sys.stdin.readline
6 |
7 | def solution():
8 | """key idea
9 | 자료구조 하나만을 활용한다면, 문자 사이에 삽입, 제거하는 연산의 복잡도가 각 O(N)이기 때문에 시간 초과가 발생할 것이다.
10 | 또, 경험상 굉장히 헷갈려서 머리아팠던 기억이 있다..
11 |
12 | 두 개의 deque을 활용해서 커서 기준 왼쪽, 오른쪽 문자열로 활용하자!
13 | deque은 double linked list기반 자료구조이기 때문에 오른쪽 왼쪽 어느 곳에서 삽입, 삭제 연산을 진행하더라도 O(1)이다.
14 | 이를 통해 효율적이면서 직관적으로 문제를 해결할 수 있다.
15 | """
16 | left = deque(input().rstrip()) # 커서 기준 왼쪽 문자열
17 | right = deque() # 커서 기준 오른쪽 문자열
18 | N = int(input())
19 | for _ in range(N):
20 | ops = input().rstrip().split()
21 | if ops[0] == 'L' and left: # 커서를 왼쪽으로 옮긴다 : 왼쪽 맨 뒤 원소를 오른쪽 첫 번째 원소로 삽입한다.
22 | right.appendleft(left.pop())
23 | if ops[0] == 'D' and right: # 커서를 오른쪽으로 옮긴다 : 오른쪽 맨 앞 원소를 왼쪽 맨 뒤 원소로 삽입한다.
24 | left.append(right.popleft())
25 | if ops[0] == 'B' and left:
26 | left.pop()
27 | if ops[0] == 'P':
28 | left.append(ops[1])
29 |
30 | print(''.join(left+right))
31 |
32 | if __name__ == "__main__":
33 | solution()
34 |
35 |
--------------------------------------------------------------------------------
/BOJ/에디터/sumin.py:
--------------------------------------------------------------------------------
1 | import sys
2 | input = sys.stdin.readline
3 |
4 | left = list(input().strip())
5 | right = []
6 | n = int(input())
7 |
8 | for i in range(n):
9 | line = input().strip().split()
10 | command = line[0]
11 |
12 | if command == 'P': # 문자를 커서 왼쪽에 추가함
13 | left.append(line[1])
14 | elif command == 'L': # 커서를 왼쪽으로 한 칸 옮김
15 | if left:
16 | right.append(left.pop())
17 | elif command == 'D': # 커서를 오른쪽으로 한 칸 옮김
18 | if right:
19 | left.append(right.pop())
20 | else: # 커서 왼쪽에 문자를 삭제함
21 | if left:
22 | left.pop()
23 |
24 | left += right[::-1]
25 | print(''.join(left))
--------------------------------------------------------------------------------
/BOJ/에디터/yerim.py:
--------------------------------------------------------------------------------
1 | # 백준 1406번: 에디터 (Silver 2)
2 | # https://www.acmicpc.net/problem/1406
3 |
4 | """
5 | 접근 방법
6 | - 처음에는, 문자열을 리스트로 변환해 pop, insert 연산 => 시간 초과가 계속 남
7 | - insert, remove 함수는 O(n)만큼의 시간을 소요하는데, 최대 명령 수가 매우 커서 시간 초과가 난다고 함
8 | - 따라서 [블로그](https://seongonion.tistory.com/53)를 참고해 커서를 기준으로 문자열을 두 곳에 나누어 담음
9 | - pop, append 연산으로 시간 초과 해결 가능!
10 | - 또한 sys를 이용해 문자열을 입력 받아야 시간 초과가 나지 않음
11 | """
12 |
13 | import sys
14 |
15 | # 커서를 기준으로 왼쪽은 str1, 오른쪽은 str2
16 | # 초기 커서 위치는 맨 뒤
17 | str1 = list(sys.stdin.readline().rstrip())
18 | str2 = []
19 |
20 | for _ in range(int(sys.stdin.readline())):
21 | cmd = list(sys.stdin.readline().split())
22 |
23 | if cmd[0] == "L":
24 | if str1:
25 | str2.append(str1.pop())
26 |
27 | elif cmd[0] == "D":
28 | if str2:
29 | str1.append(str2.pop())
30 |
31 | elif cmd[0] == "B":
32 | if str1:
33 | str1.pop()
34 |
35 | elif cmd[0] == "P":
36 | str1.append(cmd[1])
37 |
38 | # L 커맨드에서, str2에 문자가 앞으로 쌓여야 원래 순서에 맞지만,
39 | # append 연산을 위해 계속 뒤에 쌓고 마지막에 순서를 뒤집어줌
40 | str1.extend(reversed(str2))
41 | print("".join(str1))
42 |
--------------------------------------------------------------------------------
/BOJ/연속합/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 1시간 풀이 후 실패로 답지 참고
5 |
6 | 접근법
7 | - N 이 십만 이하 -> 최대 O(NlogN) 으로 접근
8 | - 연속된 몇개의 수 -> 순서는 보존된 상태로 풀어내야함
9 | - 부분합은 중복되는 계산이 굉장히 많음 -> DP로 해결해보자
10 |
11 | - 틀린 풀이
12 | - 고르는 숫자의 개수(n) 에 대한 dp 테이블을 만들고 값으로는 최대값을 넣어보자
13 | - 여러 예제를 만들어본 결과 배열 내 최대값은 고정으로 들어가는 것 같음
14 | - dp[1] 에 가장 큰 수를 넣으면 가장 큰 수를 고정으로 넣은 것과 같음
15 | - 따라서 고르는 숫자의 개수가 1개 늘어나면 왼쪽에 하나 추가한거랑 오른쪽에 하나 추가한거 비교해서 더 큰것으로 선택
16 | - 의사 코드 (점화식?)
17 | ```
18 | left_sum = dp[i-1] + arr[left_idx - 1]
19 | right_sum = dp[i-1] + arr[right_idx + 1]
20 | dp[i] = max(left_sum, right_sum)
21 | left_idx 또는 right_idx 업데이트
22 | ```
23 | - 주어진 예제 외에도 테스트케이스를 10개 정도 만들어서 돌려봤는데도 잘 나오는데, 제출하면 틀림 ...
24 |
25 | - 정답 풀이
26 | - 그냥 왼쪽부터 순회하며 이전값 + 현재값이 더 크면 연속합 갱신해주고, 현재값이 더 크면 이전 합들을 다 버리고 새로 시작하면 됨
27 |
28 | 회고
29 | - DP 문제는 테이블 설정 또는 점화식 접근을 잘 못하는 순간 완전 잘못된 방향으로 가는 것 같다
30 | - 다른 유형들에 비해 특히나 더 사전에 생각을 많이하고 풀이 시작하기
31 |
32 | """
33 |
34 | import sys
35 |
36 | inputs = sys.stdin.readline
37 | n = int(inputs())
38 | arr = list(map(int, inputs().rstrip().split()))
39 |
40 | for i in range(1, n):
41 | arr[i] = max(arr[i], arr[i-1] + arr[i])
42 |
43 | print(max(arr))
44 |
--------------------------------------------------------------------------------
/BOJ/연속합/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-10 16:19
3 |
4 | - 제한 사항
5 | - 수는 100,000개까지 주어지므로 O(NlogN) 알고리즘을 설계해야 한다.
6 | - 풀이 방법
7 | - 별도의 dp배열을 활용해 해당 배열의 i번째 원소는 수열에서 i번째부터 연속 합을 구했을 때의 가장 큰 합을 저장한다.
8 | - 이를 위해 dp 배열을 순회하며 i-1번째 까지의 최대 연속합에 i번째 원소를 더할 것인지, i번쨰 원소부터 다시 시작할 지 결정해야 한다.
9 | - 둘 중 최대값을 선택해 업데이트 한다. (`dp[i] = max(dp[i-1]+nums[i], nums[i])`)
10 | - 주의 사항
11 | - dp[i]반쩨 깂은 연속 합을 위해서 dp[i-1]에 좋든 싫든 수열의 i번째 원소를 고려한 값으로, dp배열의 마지막 값이 문제의 해는 아니다.
12 | - 즉, dp배열의 최대값이 문제의 해이다.
13 |
14 | 풀이 완료 : 2023-09-10 16:50 (풀이 시간 : 31분)
15 | """
16 |
17 | import sys
18 |
19 | input = sys.stdin.readline
20 |
21 | n = int(input())
22 | numbers = list(map(int, input().rstrip().split()))
23 | dp = [0 for _ in range(n)] # dp의 i번째 원소는 수열의 i번째 원소부터 고려했을 때 최대 연속 합
24 | answer = dp[0] = numbers[0]
25 |
26 | for i in range(1, n):
27 | dp[i] = max(
28 | dp[i - 1] + numbers[i], numbers[i]
29 | ) # i-1번째 까지 연속합에 i번째 원소를 더할 것인지, i번쨰 원소부터 다시 시작할 지 결정
30 | answer = max(dp[i], answer) # 문제의 해는 dp 배열의 최대값
31 |
32 | print(answer)
33 |
--------------------------------------------------------------------------------
/BOJ/연속합/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 10분
3 |
4 |
5 | - n(1 ≤ n ≤ 100,000)
6 | - n개의 정수로 이루어진 수열(수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수)
7 |
8 |
9 | D[i] : i번째 수로 끝나는 가장 큰 연속합
10 | 1) i-1번째 수의 연속합에 포함되는 경우
11 | - D[i-1] + A[i]
12 | 2) 새로운 연속합을 시작하는 경우
13 | - A[i]
14 |
15 | D[i] = max(D[i-1]+A[i], A[i])
16 |
17 | <시간복잡도>
18 | O(N)
19 | """
20 | import sys
21 | input = sys.stdin.readline
22 |
23 | n = int(input())
24 | a = list(map(int, input().split()))
25 | d = [0] * n
26 | d[0] = a[0]
27 | for i in range(1, n):
28 | d[i] = a[i]
29 | if d[i] < d[i-1] + a[i]:
30 | d[i] = d[i-1] + a[i]
31 | print(max(d))
--------------------------------------------------------------------------------
/BOJ/연속합/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 연속합
3 | https://www.acmicpc.net/problem/1912
4 |
5 | 풀이시간
6 | 00:57 ~ 01:44 (47분)
7 |
8 | 문제 조건
9 | 1 <= N <= 100,000
10 |
11 | 시간 복잡도 : O(N)
12 |
13 | 접근법
14 | 무슨 알고리즘으로 풀이 할 수 있을까? -> DP
15 | DP 테이블 : 현재 인덱스까지 중에서 가장 높은 값 저장하는 테이블
16 |
17 | DP 테이블을 업데이트하며 최대 값 업데이트
18 |
19 | """
20 | import sys
21 |
22 | input = sys.stdin.readline
23 |
24 | N = int(input())
25 | n_array = list(map(int, input().split()))
26 |
27 | # DP 테이블 = 현재 인덱스까지 중에 최대값
28 | dp = [0] * N
29 |
30 | # 초기 값 세팅
31 | dp[0] = n_array[0]
32 | max_value = dp[0]
33 |
34 | # 1번째 인덱스 부터 N-1까지 데이터를 순회하며 DP 테이블 업데이트 후 최대 값 기억하기
35 | for i in range(1, N):
36 | dp[i] = n_array[i]
37 | if dp[i] < dp[i - 1] + n_array[i]:
38 | dp[i] = dp[i - 1] + n_array[i]
39 | if dp[i] > max_value:
40 | max_value = dp[i]
41 |
42 | print(max_value)
43 |
--------------------------------------------------------------------------------
/BOJ/예산/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분
5 |
6 | 접근법
7 | - N이 1만이하 -> O(NlogN) 쯤의 복잡도로 해결해야함
8 | - 특정 값과 비교해서 조건을 오바하면 줄이기, 조건보다 부족하면 늘리기 + O(NlogN)? -> 이진탐색
9 |
10 | 회고
11 | - 순간 헷갈려서 시작점을 예산요청의 최소값으로 설정하니 특정 케이스(47%)에서 에러가 계속 발생했음
12 | - 1로 설정하여 해결 -> 이것때메 버벅이느라 시간 낭비 ㅠㅠ
13 |
14 | """
15 |
16 | import sys
17 |
18 | inputs = sys.stdin.readline
19 |
20 | N = int(inputs())
21 | demands = list(map(int, inputs().split()))
22 | M = int(inputs())
23 |
24 | demands.sort() # 이진탐색을 위한 정렬
25 | start, end = 1, demands[-1] # 최소 예산부터 최대 예산까지 탐색점 설정
26 |
27 | answer = end
28 |
29 | while start <= end: # 다 찾을 때까지 반복
30 | mid = (start + end) // 2
31 | temp_sum = 0
32 |
33 | for demand in demands:
34 | if demand >= mid: # 상한액보다 크다면 상한액으로 치환
35 | demand = mid
36 | temp_sum += demand
37 |
38 | if temp_sum > M: # 합이 예산보다 크다면 상환액을 조금 더 줄여야 함
39 | end = mid - 1
40 |
41 | else: # 그렇지 않다면 조건 만족
42 | answer = mid # 하지만 최대라는 보장은 없으므로 값만 업데이트하고 계속 진행
43 | start = mid + 1 # 더 크게 상한액을 설정할 수 있는지를 확인하기 위해 시작점을 늘려봄
44 |
45 | print(answer)
--------------------------------------------------------------------------------
/BOJ/예산/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-24 14:02
3 |
4 | #### 제한 사항
5 |
6 | - 3 <= N <= 10,000, 각 수는 1이상 100,000 이하이다.
7 | - 지방의 수와 예산을 계산하는 부분에 대한 알고리즘을 O(NlogN)으로 설계해야 한다.
8 |
9 | #### 풀이
10 | - 예산의 상한을 maximum으로 하고, 배정 가능한 최대 maximum을 구하면 된다.
11 | - 상한으로 가능한 범위는 low, high = 0, max(requests)
12 | - 이분 탐색으로 upperbound(예산을 배정 가능한 최대 maximum)를 구하면 된다.
13 | - upperbound를 구했으면 이를 적용해서 배정한 최대 예산을 출력한다.
14 |
15 | #### 시간 복잡도
16 | - maximum 탐색(이분탐색) + 예산 배정 가능 여부 = O(logN) * O(N) = O(NlogN)
17 |
18 | 풀이 완료 : 2023-09-24 14:12 (풀이 시간 : 10분 소요)
19 |
20 |
21 | 메모리 | 32276KB
22 | -- | --
23 | 시간 | 72ms
24 | """
25 |
26 | import sys
27 |
28 | input = sys.stdin.readline
29 |
30 |
31 | def can_assign(maximum: int) -> bool:
32 | """
33 | 예산의 상한을 maximum으로 했을 때 전체 예산을 배정 가능한지 여부를 반환한다.
34 | """
35 | total = 0
36 | for request in requests:
37 | total += min(maximum, request)
38 |
39 | return total <= M
40 |
41 |
42 | N = int(input())
43 | requests = list(map(int, input().rstrip().split()))
44 | M = int(input())
45 |
46 | low, high = 0, max(requests)
47 |
48 | while low <= high:
49 | mid = (low + high) // 2
50 |
51 | if can_assign(mid): # 예산을 배정 가능하면
52 | low = mid + 1 # 상한을 높혀서 탐색
53 | else: # 배정 불가능 하면
54 | high = mid - 1 # 상한을 낮춰서 탐색
55 |
56 | print(high) # 예산의 상한이 문제의 해
57 |
--------------------------------------------------------------------------------
/BOJ/예산/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간 : 20분
3 |
4 |
5 | - n: 지방의 수(N은 3 이상 10,000)
6 | - 지방의 예산요청을 표현하는 N개의 정수(값들은 모두 1 이상 100,000 이하)
7 | - m: 총 예산(N 이상 1,000,000,000 이하)
8 |
9 |
10 | 1) 이분 탐색을 통해 정수 상한액을 찾는다. (최적의 상한액을 찾기 위해 범위를 좁혀나가기)
11 | - 정수 상한액보다 더 많은 예산 요청을 한 경우 -> 해당 지방은 상한액을 배정한다.
12 | - 정수 상한액 이하의 예산 요청을 한 경우 -> 해당 지방은 요청 금액을 배정한다.
13 |
14 | 2) 각 지방에 모두 예산을 배정한 후 left 또는 right를 조정해 가능한 최대의 총 예산을 사용하는 방법을 찾는다.
15 | - 배정한 예상의 총합이 총 예산보다 작거나 같면, left를 증가시켜 정수 상한액을 증가시킴으로써 배정할 예산의 총합을 더 키운다.
16 | - 배정한 예상의 총합이 총 예사보다 크다면, right를 증가시켜 정수 상한액을 줄이고 배정할 예산의 총합도 줄여준다.
17 |
18 | 3) 최종적으로 배정된 예산들 중 최댓값(정수 상한액)인 정수를 출력한다.
19 |
20 | <시간복잡도>
21 | O(nlogn)
22 | """
23 | import sys
24 | input = sys.stdin.readline
25 |
26 |
27 | n = int(input()) # 지방의 수
28 | cities = list(map(int, input().split())) # 각 지방의 예산 요청 금액
29 | budget = int(input()) # 총 예산
30 |
31 |
32 | left, right = 0, max(cities)
33 | while left <= right:
34 | mid = (left+right) // 2 # 정수 상한액
35 | tot = 0 # 배정한 예산의 총합
36 | for city in cities:
37 | if city > mid: # 1) 상한액 이상의 예산 요청을 한 경우
38 | tot += mid # 상한액 배정
39 | else: # 2) 상한액 이하의 예산 요청을 한 경우
40 | tot += city # 요청한 금액
41 | if tot <= budget: # 배정한 예산의 총합이 총 예산보다 적다면
42 | left = mid + 1 # 정수 상한액을 증가시키기 -> 배정할 예산의 총합도 커짐
43 | else: # 배정한 예산의 총합이 총 예산보다 크다면
44 | right = mid - 1 # 정수 상한액을 줄이기 -> 배정할 예산의 총합도 줄어듬
45 |
46 | # 배정된 예산들 중 최댓값인 정수
47 | print(right)
48 |
--------------------------------------------------------------------------------
/BOJ/예산/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 예산
3 | https://www.acmicpc.net/problem/2512
4 |
5 | 풀이시간
6 | 10 : 20 ~ 10 : 32 (12분)
7 |
8 | 문제 조건
9 | 3 <= N <= 10,000
10 | 1 <= x <= 100,000
11 |
12 | 시간 복잡도 :
13 | O(N + logx * N)
14 |
15 | 접근법
16 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 이진 탐색
17 |
18 | 목표하는 상한액을 찾기 위해서 1 ~ 100,000안의 값을 찾아야 하기 때문에 이진 탐색을 통해서 탐색합니다.
19 | """
20 | import sys
21 |
22 | input = sys.stdin.readline
23 |
24 | def binary_search(start, end):
25 | answer = 0
26 |
27 | while start <= end:
28 | mid = (start + end) // 2
29 |
30 | # 정수 상한액을 적용하여 sum 적용한 결과
31 | temp_sum = sum([x if x <= mid else mid for x in budget])
32 |
33 | # 합이 국가 총 예산보다 크다면 상한액 줄이기
34 | if temp_sum > target_sum:
35 | end = mid - 1
36 | # 국가 총 예산액보다 작거나 같다면 상한액 늘리기
37 | elif temp_sum <= target_sum:
38 | start = mid + 1
39 | answer = mid
40 |
41 | return answer
42 |
43 | # 지방의 개수
44 | N = int(input())
45 |
46 | # 지방의 각 예산
47 | budget = list(map(int, input().split()))
48 |
49 | # 요청 받은 지방의 예산 중 최대 값
50 | max_budget = max(budget)
51 |
52 | # 국가 총 예산
53 | target_sum = int(input())
54 |
55 | # 국가 총 예산이하라면 정수 상한액은 예산 중 최대 값
56 | if target_sum >= sum(budget):
57 | print(max_budget)
58 | # 아니라면 이진 탐색
59 | else:
60 | print(binary_search(0, max_budget))
--------------------------------------------------------------------------------
/BOJ/잃어버린 괄호/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분
5 |
6 | 접근법
7 | - 입력으로 주어지는 수의 길이 <= 50 -> 구현에 집중
8 | - 값을 최소로 만들어야 한다 -> - 뒤의 값을 괄호를 통해 최대한 크게 만들어야 함 -> 그리디
9 |
10 | 회고
11 | - 처음에는 str, int 가 섞인 상태로 진행했는데 같은 자료형(정수)로 한꺼번에 변환하는 방법이 훨씬 효율적인 것 같음
12 | - 정수로 반환하는 과정에서 최대한 내장함수를 쓰려다가 조금 버벅거림 ...
13 | - 풀고나서 드는 생각이 한꺼번에 정수로 변환해놓고 시작하지말고 반복문으로 변환해가면서 연산하는 방법이 조금 더 빠르긴 할 것 같다
14 |
15 | """
16 |
17 | import sys
18 |
19 | inputs = sys.stdin.readline
20 |
21 | example = inputs().rstrip()
22 | arr = []
23 | temp_num = ""
24 | for item in example: # 문자열을 정수로 변환하여 리스트화 (-> [55, -50, 40])
25 | if item in ["-", "+"]:
26 | arr.append(int(temp_num))
27 | temp_num = item
28 | else:
29 | temp_num += item
30 |
31 | arr.append(int(temp_num)) # 맨 마지막 숫자 추가
32 | arr.append(0) # 인덱스에러가 나지 않도록 연산에 영향을 주지 않는 0 하나 추가
33 |
34 | answer = 0
35 |
36 | for i in range(len(arr)-1):
37 | answer += arr[i]
38 | if (arr[i] < 0) and (arr[i+1] > 0): # 현재 수가 음수인데 다음 수가 양수이면 괄호처리
39 | arr[i+1] *= -1
40 |
41 | print(answer)
--------------------------------------------------------------------------------
/BOJ/잃어버린 괄호/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-11 14:20
3 |
4 | #### 제한 조건
5 | - 주어지는 식의 길이는 최대 50
6 |
7 | #### 풀이 방법
8 | - 연산자는 '+' 아니면 '-'
9 | - 맨 앞에 -가 오는 경우는 없으므로 여는 괄호는 연산자 뒤, 닫는 괄호는 다음 연산자 앞에 올 수 있음
10 | - 최소 값을 만들기 위해 '-' 다음에 오는 구간을 최대로 만들어줘야 함
11 | - '-'연산자 뒤에 괄호를 열고 다음 '-'가 나오기 전에 괄호를 닫으면 '-' 구간을 최대로 묶을 수 있음
12 | - 55-50+40-10+20+30+40 -> 55-(50+40)-(10+20+30+40)
13 |
14 | 풀이 완료 : 2023-09-11 14:58 (풀이 시간 : 38분)
15 | """
16 |
17 | import sys
18 | from collections import deque
19 |
20 | input = sys.stdin.readline
21 |
22 | equation = deque(input().rstrip()) # 숫자 문자열 파싱을 위해 큐를 활용(연산자 등장 전까지 popleft())
23 | braket_state = False # 현재 괄호가 열려있는지 여부
24 | after = "" # 괄호 추가 후 문자열
25 | tmp = "" # 유효한 숫자 문자열을 파싱할 임시 변수
26 |
27 | while equation:
28 | while equation and equation[0] not in ["+", "-"]: # 다음 문자가 연산자일 때까지
29 | tmp += equation.popleft() # 문자 파싱
30 | after += str(int(tmp)) # 유효한 숫자 문자열로 변환 (00009 -> 9)
31 | tmp = ""
32 |
33 | if not equation: # 다음 문자가 남아있지 않으면 break
34 | break
35 |
36 | if equation[0] == "-": # 다음 문자가 남아있다면 연산자임, '-'인 경우
37 | if braket_state: # 괄호가 열린 상태에서
38 | after += (
39 | ")" + equation.popleft() + "("
40 | ) # 닫고 '-' 추가 후 다시 열어줌(연산자가 마지막일 순 없음)
41 | else: # 괄호가 닫혀 있는 상태에서는
42 | after += equation.popleft() + "(" # '-' 추가 후 열어줌
43 | braket_state = True # 괄호 열림 표시
44 | else: # 다음 연산자가 '+'인 경우
45 | after += equation.popleft() # 그냥 추가해주면 됨
46 |
47 | if braket_state: # 위 로직대로라면 한 번 괄호 열렸으면 닫히지 않으므로 해당 과정 추가
48 | after += ")"
49 |
50 | print(eval(after))
51 |
--------------------------------------------------------------------------------
/BOJ/잃어버린 괄호/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 10분
3 |
4 |
5 | 숫자 0~9, +, -로 이루어진 최대 길이 50인 식
6 | 가장 처음과 마지막 문자는 숫자
7 |
8 |
9 | 어떻게 괄호를 넣어야 수식의 값이 최소값이 될 수 있을까?
10 | -를 기준으로 괄호를 넣을 때 수식의 결과가 가장 작아질 수 있음
11 | ex. 5-35+20-45라면 5-(35+20)-45처럼 -와 - 사이의 35+20을 빼주는 것이 되기 때문에!
12 |
13 | <시간 복잡도>
14 | O(N)
15 | """
16 | exp = input().split('-')
17 |
18 | nums = [] # + 연산을 처리한 숫자들
19 | for x in exp:
20 | num = 0 # 괄호로 묶은 수식들의 결과값
21 | s = x.split('+') # +가 있는 경우를 위해 다시 +로 split
22 | for j in s: # '+'가 없을 때 split하면 원래값 반환됨
23 | num += int(j)
24 | nums.append(num)
25 |
26 | ans = nums[0] # 최종 결과값
27 | for i in nums[1:]: # 수를 순회하며 ans에서 빼주면 된다.
28 | ans -= i
29 |
30 | print(ans)
31 |
--------------------------------------------------------------------------------
/BOJ/잃어버린 괄호/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 잃어버린 괄호
3 | https://www.acmicpc.net/problem/1541
4 |
5 | 풀이시간
6 | 11:28 ~ 12:00(32분)
7 |
8 | 문제 조건
9 | 식의 길이가 최대 50
10 | 25개의 한 자리 숫자와 24개의 연산이 있을 때
11 |
12 | 시간 복잡도 : O(25 * 2)가 최대
13 |
14 | 접근법
15 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 문자열
16 |
17 | 최솟값을 사용해야하기 때문에 음의 항을 최대화 하는 것이 중요하다.
18 |
19 | - 만약 이전 항이 음수항이었다면 다음 음수항이 나올때까지 모든 요소들을 괄호에 추가하기
20 | """
21 | import sys
22 |
23 | input = sys.stdin.readline
24 |
25 | # 방정식 입력받기
26 | equation = input()
27 |
28 | # "-" 를 기준으로 음수항을 정의
29 | terms = equation.split("-")
30 |
31 | # 항들에는 앞자리에 0이 채워질 수 있기 때문에 각 항들은 int로 형 변환 후 덧셈 진행
32 | for idx, term in enumerate(terms):
33 | t_term = list(map(int, term.split("+")))
34 | terms[idx] = sum(t_term)
35 |
36 | # 첫 번째 항은 항상 양수
37 | answer = terms[0]
38 |
39 | # 두 번째 항부터는 음수
40 | for term_idx in range(1, len(terms)):
41 | answer -= terms[term_idx]
42 |
43 | print(answer)
--------------------------------------------------------------------------------
/BOJ/제로/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 4분
5 |
6 | 접근법
7 | - 제한시간 1초 + 입력 K 가 최대 10만번이기 때문에 시간 복잡도 보다는 빠르게 구현하는걸 연습해보자
8 | - 익숙해지도록 deque 를 활용해서 풀이해보려고 했음, 근데 왜이렇게 쉽지??? 이게맞나??? 하면서 풀이
9 |
10 | 회고
11 | - 몬가 참신한 다른 풀이가 보고싶다!
12 |
13 | """
14 |
15 | import sys
16 | from collections import deque
17 |
18 | question = sys.stdin.readline # 문자열 입력받음
19 |
20 | K = int(question()) # 반복 횟수 K 입력받음
21 | total_money = deque() # 장부 리스트 정의
22 |
23 | for i in range(K):
24 | money = int(question()) # 돈 입력받음
25 | if money==0:
26 | total_money.pop() # 가장 최근에 입력된 돈을 제거 -> pop
27 | else:
28 | total_money.append(money) # 최근순으로 돈을 추가
29 |
30 | print(sum(total_money))
--------------------------------------------------------------------------------
/BOJ/제로/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 21:36 -> 21:40
3 |
4 | Key Idea : 리스트를 스택으로 사용
5 | K번 수를 입력받아 0인 경우 스택에서 마지막 원소 pop, 이외에는 스택에 해당 수를 추가
6 |
7 | 이는 입력 받은 수가 0인 경우 스택이 비어있지 않음이 보장되어 있기 때문에 가능하다
8 | 이게 보장되어있지 않다면, pop을 할 때마다 스택이 비어있는지를 검사해야 한다.
9 | """
10 |
11 | import sys
12 |
13 | input = sys.stdin.readline
14 |
15 | def main():
16 |
17 | K = int(input().rstrip()) # K번 입력받을 것입니다
18 | stack = []
19 |
20 | for _ in range(K): # K번 입력 받기
21 | num = int(input().rstrip())
22 | stack.pop() if num == 0 else stack.append(num) # 입력 받은 수가 0이면 스택의 마지막 원소 pop, 이외에는 추가
23 | # 0일 때는 스택이 비어있지 않음이 보장되어있으니 가능한 코드
24 |
25 | print(sum(stack)) # 최종 스택 내 수들의 합 출력
26 |
27 | main()
28 |
--------------------------------------------------------------------------------
/BOJ/제로/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | <풀이 시간>
3 | 3분
4 |
5 |
6 | k : 1 ≤ K ≤ 100,000
7 |
8 |
9 | 전형적인 stack 문제
10 | 1) 0이 입력되면, stack의 top을 제거
11 | 2) 0이 아닌 경우, 해당 수를 stack에 추가
12 | 3) 적어낸 수의 합을 sum(stack)으로 구할 수 있지만, 또 O(k)의 연산을 할 필요없이 입력값이 들어왔을 때 바로바로 갱신해주기
13 | - 사소한 부분이지만 위 방식의 경우 84ms가 걸리고, sum(stack)의 경우 112ms가 걸렸음
14 |
15 | """
16 | import sys
17 | input = sys.stdin.readline
18 |
19 | k = int(input()) # 1 ≤ K ≤ 100,000
20 |
21 | ans = 0 # 최종적으로 적어낸 수의 합
22 | stack = []
23 | for _ in range(k):
24 | num = int(input()) # 정수
25 | if num == 0: # 가장 최근에 쓴 수를 지운다 -> stack의 top 제거
26 | ans -= stack.pop()
27 | else: # 0이 아니라면 해당 수를 쓴다 -> stack에 append
28 | stack.append(num)
29 | ans += num
30 |
31 | print(ans)
32 |
--------------------------------------------------------------------------------
/BOJ/제로/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 제로
3 | https://www.acmicpc.net/problem/10773
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 단순구현
7 | """
8 |
9 | import sys
10 | input = sys.stdin.readline
11 |
12 | K = int(input().strip())
13 |
14 | # Targets 리스트를 스택으로 사용한다.
15 | targets = []
16 | for _ in range(K):
17 | target = int(input().strip())
18 |
19 | # target == 0 이라면 target append 아니라면 pop
20 | if target:
21 | targets.append(target)
22 | else:
23 | targets.pop()
24 |
25 | # 전체 원소 덧셈 값 반환
26 | print(sum(targets))
--------------------------------------------------------------------------------
/BOJ/제로/yerim.py:
--------------------------------------------------------------------------------
1 | # 백준 10773번: 제로 (Silver 4)
2 | # https://www.acmicpc.net/problem/10773
3 | # 소요 시간 : 10분
4 |
5 | k = int(input())
6 | nums = []
7 | for i in range(k):
8 | num = int(input())
9 | if num: # num이 0이 아니면 해당 수를 쓴다
10 | nums.append(num)
11 | else: # num이 0이면 최근에 쓴 수를 지운다
12 | nums.pop()
13 |
14 | print(sum(nums))
15 |
--------------------------------------------------------------------------------
/BOJ/종이의 개수/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023-08-18 11:03
3 |
4 | N은 3^k 꼴로 주어짐
5 |
6 | 3^k * 3^k 를 순회하여 모두 같은 수인지 확인 -> 같다면 해당 수 종이 += 1
7 | -> 같지 않다면 9등분 -> 3^(k-1) * 3^(k-1) 를 순회하며 반복
8 | -> 이전 문제와 마찬가지로 각 영역에 해당하는 인덕스를 적절하게 조절하여 재귀해줘야 함
9 | 풀이 완료 : 2023-08-18 11:45 (풀이 소요 시간 : 42분)
10 |
11 | '''
12 | import sys
13 | from typing import List, Dict
14 |
15 | input = sys.stdin.readline
16 |
17 | def solution() -> None:
18 | solutionDict:Dict[int, int] = {-1:0, 0:0, 1:0} # 각 종이의 수를 담을 dict
19 | N: int = int(input())
20 | matrix: List[List[int]] = [list(map(int, input().rstrip().split())) for _ in range(N)]
21 | k: int = 0
22 |
23 | while N % 3 == 0: # k 구하기
24 | k+=1
25 | N//=3
26 |
27 | def recur(k, rStart, rEnd, cStart, cEnd): # 재귀함수, row의 시작과 끝, col의 시작과 끝을 인자로 받음
28 | # 9등분 한 각각의 영역을 독립적으로 생각하기 위함
29 | initValue: int = matrix[rStart][cStart] # 한 영역이 전부 같은 수임을 판별하기 위한 변수
30 |
31 | for r in range(rStart, rEnd):
32 | for c in range(cStart, cEnd):
33 | if matrix[r][c] != initValue: # 해당 영역이 하나라도 같지 않으면 9등분
34 | # k가 2(N=9)라고 생각해보면 r과 c가 각각 (0~2, 3~5, 6~8)인 9 영역으로 나뉘어야 함
35 | for rDiv3 in range(rStart, rEnd, 3**(k-1)):
36 | for cDiv3 in range(cStart, cEnd, 3**(k-1)):
37 | recur(k-1, rDiv3, rDiv3+3**(k-1), cDiv3, cDiv3+3**(k-1))
38 | return
39 |
40 | solutionDict[matrix[rStart][cStart]] += 1 # 전부 같은 수이면 초기값에 해당하는 종이가 한 장
41 |
42 | recur(k, 0, 3**k, 0, 3**k)
43 |
44 | for answer in solutionDict.values():
45 | print(answer)
46 |
47 | solution()
--------------------------------------------------------------------------------
/BOJ/종이의 개수/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 30분
3 |
4 |
5 | N(1 ≤ N ≤ 37, N은 3k 꼴) -> N x N 행렬
6 |
7 |
8 | 1. (x, y, n)이 같은 수로 돼있는지 확인
9 | 2. 그렇지 않으면 9등분하고 다시 확인
10 |
11 | <시간복잡도>
12 | O(n^2 * log_3(n))
13 | """
14 |
15 | # 해당 종이 내부에 같은 숫자로만 채워졌는지 확인하는 함수
16 | def check(x, y, n):
17 | for i in range(x, x+n):
18 | for j in range(y, y+n):
19 | if paper[x][y] != paper[i][j]:
20 | return False
21 | return True
22 |
23 | # (x, y)부터 가로로 n개, 세로로 n개의 종이 개수를 확인하는 함수
24 | def solve(x, y, n):
25 | if check(x, y, n): # 만약 같은 숫자로만 이루어져있다면
26 | cnt[paper[x][y]+1] += 1 # 해당 숫자의 개수 갱신
27 | return
28 | m = n // 3
29 | # 9등분
30 | for i in range(3):
31 | for j in range(3):
32 | solve(x+i*m, y+j*m, m)
33 |
34 |
35 | n = int(input())
36 | paper = [list(map(int, input().split())) for _ in range(n)] # 종이
37 | cnt = [0] * 3 # -1, 0, 1로 채워진 종이 갯수
38 | solve(0, 0, n)
39 | print('\n'.join(map(str, cnt)))
40 |
--------------------------------------------------------------------------------
/BOJ/카드2/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 5분
5 |
6 | 접근법
7 | - 모든 동작 방식이 deqeue 로 구현가능
8 | - 특정 조건 만족까지 반복되는 행위 -> 반복문/재귀로도 풀이 가능
9 |
10 | 회고
11 | - 쉬웠던 것 같아서 재귀 풀이도 하나 넣어봤습니다!!
12 | - 재귀문이 시간이 2배 이상 걸리는데 원래 그런걸까용?
13 |
14 | """
15 |
16 | ### 반복문 풀이
17 | from collections import deque
18 |
19 | N = int(input()) # 정수 N 입력받음
20 | queue = deque([i for i in range(1, N+1)]) # 카드 덱 구성
21 |
22 | while True:
23 | if len(queue)==1:
24 | print(queue[0])
25 | break
26 | queue.popleft() # 맨 위에 있는 카드 버림
27 | out = queue.popleft() # 그 다음 위에 있는 카드를 선택
28 | queue.append(out) # 해당 카드를 맨 밑으로 옮김
29 |
30 |
31 | ### 재귀 풀이
32 | import sys
33 | sys.setrecursionlimit(10 ** 6) # 재귀 깊이 제한을 풀어줌 (재귀문제에서 필수라고 함)
34 | from collections import deque
35 |
36 | N = int(input())
37 | queue = deque([i for i in range(1, N+1)])
38 |
39 | def run(cards):
40 | if len(cards) == 1:
41 | print(cards[0])
42 | else:
43 | cards.popleft()
44 | out = cards.popleft()
45 | cards.append(out)
46 | run(cards)
47 |
48 | run(queue)
--------------------------------------------------------------------------------
/BOJ/카드2/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 16:30 -> 16:35
3 |
4 | 1 <= N <= 500,000 이므로 O(N) 안에 해결
5 | '''
6 |
7 | import sys
8 | from collections import deque
9 |
10 | input = sys.stdin.readline
11 |
12 | def main():
13 |
14 | N = int(input())
15 | deq = deque(range(1, N+1)) # N(1~N)장의 카드 구성
16 |
17 | while len(deq) > 1: # 카드가 1장 남을 때까지 아래 반복
18 | deq.popleft() # 제일 위에 있는 카드 바닥에 버리기
19 | deq.append(deq.popleft()) # 제일 위에 있는 카드를 제일 아래 있는 카드 밑으로 옮기기
20 |
21 | print(deq[0]) # 제일 마지막에 남게 되는 카드
22 |
23 | if __name__ == '__main__':
24 | main()
25 |
26 |
--------------------------------------------------------------------------------
/BOJ/카드2/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 3분
3 |
4 |
5 | N: 정수 N(1 ≤ N ≤ 500,000)
6 |
7 |
8 | 시간 복잡도: O(N) <- while문이 최대 n-1번 반복되기 때문에
9 | 문제 조건대로 구현하면 된다.
10 | """
11 | from collections import deque
12 |
13 |
14 | n = int(input())
15 | q = deque(range(1, n+1)) # 큐
16 |
17 | while True:
18 | if len(q) == 1:
19 | print(q[0])
20 | break
21 | q.popleft() # 1. 맨 위의 카드를 뽑는다.
22 | q.append(q.popleft()) # 2. 그 다음 위의 카드를 뽑는다.
--------------------------------------------------------------------------------
/BOJ/카드2/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 카드2
3 | https://www.acmicpc.net/problem/2164
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> deque을 사용하여 O(1)연산을 반복 실행한다.
7 |
8 | 50만회가 최대이기 때문에 단순 구현이 가능하였고 이때에 pop, append 연산이 O(1)이어야한다.
9 | 따라서 deque를 사용하여 구현한다면 시간 초과가 걸리지 않는다.
10 |
11 | len() 함수를 매번호출하지 않고 length를 기억하는 변수를 사용하였더니 시간이 (240 -> 212 ms)로 단축되었습니다.
12 | """
13 | import sys
14 | from collections import deque
15 |
16 | input = sys.stdin.readline
17 |
18 | N = int(input())
19 |
20 | # 1 ~ N의 카드를 담은 cards 행렬을 deque로 구현한다.
21 | cards = deque([num for num in range(1, N + 1)])
22 | cards_length = len(cards)
23 |
24 | # cards의 갯수가 1개가 될 때 까지 다음의 과정을 반복한다.
25 | while cards_length > 1:
26 | # 1. cards의 가장 왼쪽의 값을 제거한다.
27 | cards.popleft()
28 | # 2. cards의 가장 왼쪽의 값을 제거하여 우측의 끝에 삽입한다.
29 | cards.append(cards.popleft())
30 | cards_length -= 1
31 |
32 | print(cards[0])
33 |
--------------------------------------------------------------------------------
/BOJ/카드2/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | [백준: 카드2 (Silver 4)](https://www.acmicpc.net/problem/2164)
3 |
4 | - 풀이 시간: 10분
5 | - 접근 방법:
6 | - popleft 활용을 위해 deque으로 구현
7 |
8 | """
9 |
10 | import sys
11 | from collections import deque
12 |
13 | input = sys.stdin.readline
14 |
15 | cards = deque([n for n in range(1, int(input()) + 1)])
16 |
17 | for _ in range(len(cards) - 1): # N장의 카드를 (N-1)번 버려야 최종으로 1장이 남음
18 | cards.popleft() # 제일 위에 있는 카드를 버림
19 | cards.append(cards.popleft()) # 제일 위에 있는 카드를 제일 아래로 옮김
20 |
21 | print(cards[0])
22 |
--------------------------------------------------------------------------------
/BOJ/큐/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 10분
5 |
6 | 접근법
7 | - 문제를 읽어보니 deque 에서 전부 구현가능
8 | - 제출 전까진 시간 복잡도가 문제일까? 라는 생각을 하긴 했지만 size 연산 제외 그럴 여지는 딱히 보이지 않았음
9 | - 따라서 바로 deque 로 구현!
10 |
11 | 회고
12 | - 나름 easy 했던 문제였던 것 같은데 그대로 따라가기만 한거라 다른 분들의 디테일이 궁금하네요!
13 |
14 | """
15 |
16 | import sys
17 | from collections import deque
18 |
19 | question = sys.stdin.readline # 문자열 입력받음
20 |
21 | N = int(question()) # 반복 횟수 N 입력받음
22 | queue = deque() # 정수를 저장하는 빈 큐 정의
23 |
24 | for _ in range(N):
25 | command = question().rstrip() # 명령 입력받음
26 |
27 | if len(command.split())==2: # push 명령일 경우 숫자도 함께 들어오므로 분리
28 | command, num = command.split()
29 | num = int(num)
30 |
31 | if command=='push':
32 | queue.append(num)
33 |
34 | elif command=='front':
35 | if not queue:
36 | print(-1)
37 | else:
38 | print(queue[0])
39 |
40 | elif command=='back':
41 | if not queue:
42 | print(-1)
43 | else:
44 | print(queue[-1])
45 |
46 | elif command=='pop':
47 | if not queue:
48 | print(-1)
49 | else:
50 | out = queue.popleft()
51 | print(out)
52 |
53 | elif command=='size':
54 | print(len(queue))
55 |
56 | elif command=='empty':
57 | if not queue:
58 | print(1)
59 | else:
60 | print(0)
61 |
62 |
63 |
--------------------------------------------------------------------------------
/BOJ/큐/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 3분
3 |
4 |
5 | n: 명령의 수 (1 ≤ N ≤ 10,000)
6 |
7 | 시간복잡도: O(N)
8 | """
9 | import sys
10 | input = sys.stdin.readline
11 | from collections import deque
12 |
13 |
14 | n = int(input()) # 명령의 수
15 | q = deque()
16 | for _ in range(n): # n번의 연산 명령 입력받기
17 | op = input().split()
18 | if op[0] == 'push': # 정수 x를 큐에 넣는 연산 -> O(1)
19 | q.append(op[1])
20 | elif op[0] == 'pop': # 큐에 가장 앞에 있는 정수를 빼고, 그 수를 출력한다. 만약 큐에 들어있는 정수가 없는 경우에는 -1을 출력 -> O(1)
21 | print(q.popleft() if q else -1)
22 | elif op[0] == 'size': # 큐에 들어있는 정수의 개수를 출력 -> O(1)
23 | print(len(q))
24 | elif op[0] == 'empty': # 큐가 비어있으면 1, 아니면 0을 출력 -> O(1)
25 | print(0 if q else 1)
26 | elif op[0] == 'front': # 큐의 가장 앞에 있는 정수를 출력한다. 만약 큐에 들어있는 정수가 없는 경우에는 -1을 출력 -> O(1)
27 | print(q[0] if q else -1)
28 | else: # 큐의 가장 뒤에 있는 정수를 출력한다. 만약 큐에 들어있는 정수가 없는 경우에는 -1을 출력 -> O(1)
29 | print(q[-1] if q else -1)
--------------------------------------------------------------------------------
/BOJ/큐/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 큐
3 | https://www.acmicpc.net/problem/10845
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 단순 구현
7 |
8 | queue 자료형을 이해한다면 python deque을 사용하여 구현이 가능하다.
9 |
10 | """
11 | import sys
12 | from collections import deque
13 |
14 | input = sys.stdin.readline
15 |
16 | N = int(input())
17 |
18 | result = deque()
19 |
20 | for _ in range(N):
21 | # input commands
22 | commands = input().rstrip().split()
23 | # 각 케이스 별로 데이터 처리
24 | if commands[0] == "push":
25 | result.append(commands[1])
26 | elif commands[0] == "size":
27 | print(len(result))
28 | elif commands[0] == "empty":
29 | print(0 if result else 1)
30 | elif commands[0] == "back" and result:
31 | print(result[-1])
32 | elif commands[0] == "front" and result:
33 | print(result[0])
34 | elif commands[0] == "pop" and result:
35 | print(result.popleft())
36 | else:
37 | print(-1)
38 |
--------------------------------------------------------------------------------
/BOJ/큐/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | [백준 10845번: 큐 (Silver 4)](https://www.acmicpc.net/problem/10845)
3 | 풀이 시간: 19분
4 |
5 | [접근 방법]
6 | - queue 구현 시 연산 속도가 더 빠른 deque 사용
7 | - push의 경우 띄어쓰기 후 숫자가 입력됨 => input 값을 split 함수를 사용해 리스트로 저장
8 | - queue의 길이를 계산해야 하는 경우가 빈번하므로 미리 계산해둠
9 | - push, size, empty 연산을 제외한 나머지 연산은 큐가 비어있는 경우 공통으로 -1을 출력하므로 그에 따라 조건 분할
10 |
11 | """
12 | from collections import deque
13 | import sys
14 |
15 | input = sys.stdin.readline
16 | queue = deque()
17 | for _ in range(int(input())):
18 | cmd = input().split() # 명령 입력
19 | len_q = len(queue) # 큐 길이
20 |
21 | if cmd[0] == "push":
22 | queue.append(cmd[1]) # 큐에 숫자 삽입
23 |
24 | elif cmd[0] == "size": # 큐 길이 출력
25 | print(len_q)
26 |
27 | elif cmd[0] == "empty": # 큐 길이가 0이면 1 반환, 아니면 0 반환
28 | print(int(len_q == 0))
29 |
30 | else:
31 | if len_q == 0: # 큐가 비어있으면 -1 출력
32 | print(-1)
33 | continue
34 |
35 | elif cmd[0] == "pop": # 가장 앞에 있는 원소 삭제하며 출력
36 | print(queue.popleft())
37 |
38 | elif cmd[0] == "front": # 가장 앞에 있는 원소 출력
39 | print(queue[0])
40 |
41 | elif cmd[0] == "back": # 가장 뒤에 있는 원소 출력
42 | print(queue[-1])
43 |
--------------------------------------------------------------------------------
/BOJ/키로거/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 20분 소요
5 |
6 | 접근법
7 | - 문자열의 길이가 생각보다 길다! 시간복잡도를 줄여야 할 듯
8 | - 문제를 읽어보니 BOJ 에디터 문제의 접근과 거의 완전히 유사함
9 | - 지수님의 deque 풀이에 영감을 받아 참고해서 풀이 ㅎㅎ..
10 |
11 | 회고
12 | - 이전 문제를 잘 이해하고 넘어가면 비슷한 유형을 만났을 때 빠르게 풀이가 가능하다! 문제 대충 넘어가지말고 잘 이해하자
13 | - 이 문제는 왜인지는 모르겠지만 sys.stdin.readline() 을 사용할 때 보다 input() 을 사용한게 더 빨랐다..?
14 |
15 | """
16 |
17 | import sys
18 | from collections import deque
19 |
20 | n = int(sys.stdin.readline()) # 문자열 개수 입력
21 |
22 | for i in range(n):
23 | left = deque() # 커서 기준 왼쪽 비밀번호
24 | right = deque() # 커서 기준 오른쪽 비밀번호
25 | passwords = sys.stdin.readline().rstrip()
26 |
27 | for password in passwords: # 문자열을 처음부터 순서대로 검토
28 | if (password == '<') and left: # 커서를 왼쪽으로 옮김 + 커서가 맨 처음에 위치하는 경우 제외
29 | right.appendleft(left.pop())
30 | elif (password == '>') and right: # 커서를 오른쪽으로 옮김 + 커서가 맨 처음에 위치하는 경우 제외
31 | left.append(right.popleft())
32 | elif (password == '-') and left: # 왼쪽 문자열을 제거 + 커서가 맨 처음에 위치하는 경우 제외
33 | left.pop()
34 | elif password not in (['<', '>', '-']): # left, right 덱이 비어있을 경우 제외
35 | left.append(password)
36 |
37 | print(''.join(left+right))
--------------------------------------------------------------------------------
/BOJ/키로거/jisu.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from collections import deque
3 |
4 | input = sys.stdin.readline
5 |
6 | T = int(input()) # 테스트 케이스 수
7 |
8 | for _ in range(T):
9 | left, right = deque(), deque() # 각각 커서 왼쪽, 오른쪽 문자열을 담을 리스트(덱)
10 |
11 | ops = list(input().rstrip())
12 | for op in ops:
13 | if op == '<': # (주의) if op=='<' and left 와 같이 쓰면 left가 비어있을 때 else 분기로 가게 됨
14 | if left:
15 | right.appendleft(left.pop())
16 | elif op == '>':
17 | if right:
18 | left.append(right.popleft())
19 | elif op == '-':
20 | if left:
21 | left.pop()
22 | else:
23 | left.append(op)
24 |
25 | print(''.join(left+right)) # 커서 기준 왼쪽, 오른쪽 문자열을 합쳐서 join
26 |
--------------------------------------------------------------------------------
/BOJ/키로거/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 시간: 10분
3 | 에디터와 매우 유사한 문제로 스택 두개를 활용해 문제를 풀었다.
4 |
5 |
6 | - 문자열의 길이가 1,000,000이기 때문에 O(n)으로 해결해야 함
7 | - 링크드 리스트에 속한 문제이고, 스택 두개를 이용해 링크드 리스트를 구현할 수 있기 때문에 링크드 리스트를 구현하기보다는 스택 역할로 list를 사용했다.
8 | """
9 | import sys
10 | input = sys.stdin.readline
11 |
12 | t = int(input()) # 테스트 케이스의 개수
13 | for _ in range(t):
14 | # 길이가 L인 문자열 입력(1 <= L <= 1,000,000)
15 | pw = input().rstrip() # 문자열에는 알파벳 대소문자, 숫자, 백스페이스, <, >가 온다.
16 | left = [] # 커서의 왼쪽
17 | right = [] # 커서의 오른쪽
18 | for s in pw: # 문자를 하나씩 확인하면서
19 | # 1) 백스페이스인 경우
20 | if s == '-':
21 | if left: # 커서의 왼쪽에 문자가 있다면
22 | left.pop() # 문자를 지움
23 | # 2) < 인 경우
24 | elif s == '<':
25 | if left: # 커서의 왼쪽에 문자가 있다면
26 | right.append(left.pop()) # 커서를 한 칸 왼쪽으로 이동 -> 즉, 커서의 한 칸 왼쪽에 있던 문자가 커서의 오른쪽으로 이동하게 됨
27 | # 3) > 인 경우
28 | elif s == '>':
29 | if right: # 커서의 오른쪽에 문자가 있다면
30 | left.append(right.pop()) # 커서를 한 칸 오른쪽으로 이동 -> 즉, 커서의 한 칸 오른쪽에 있던 문자가 커서의 왼쪽으로 이동하게 됨
31 | # 4) 대소문자인 경우
32 | else:
33 | left.append(s) # 항상 커서의 왼쪽에 위치
34 | print(''.join(left+right[::-1]))
--------------------------------------------------------------------------------
/BOJ/키로거/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 키로거
3 | https://www.acmicpc.net/problem/5397
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> deque을 사용해서 cursur를 기준으로 데이터 이동
7 |
8 | 1. 커서 문제가 나오면 연결리스트 방식과 다음과 같이
9 | cursur를 기준으로 공간을 생각하여 풀이하는 방식으로 풀이 할 수 잇을 것 같았다.
10 |
11 | 결국 풀이는 두개의 덱을 사용한 이전 에디터 문제와 동일한 형식을 사용하여 풀이 할 수 있었다.
12 | """
13 | import sys
14 | from collections import deque
15 |
16 | input = sys.stdin.readline
17 |
18 | L = int(input())
19 |
20 |
21 | answer = []
22 | for _ in range(L):
23 | left,right = deque(), deque() # 두 개의 왼쪽 오른쪽 덱을 구현
24 | sentence = input().strip()
25 | for c in sentence:
26 | if c == '<' and left: # < 연산자는 왼쪽 덱의 오른쪽 요소를 오른쪽 덱의 왼쪽 삽입
27 | right.appendleft(left.pop())
28 | elif c == '>' and right: # > 연산자는 오른쪽 덱의 왼쪽 요소를 왼쪽 덱의 오른쪽 요소에 삽입
29 | left.append(right.popleft())
30 | elif c == '-' and left: # - 연산자는 왼쪽 덱의 오른쪽 요소 제거
31 | left.pop()
32 | elif c not in ('<','>','-'): # 연산자가 아닌 문자는 왼쪽 덱의 오른쪽에 삽입
33 | left.append(c)
34 | answer.append(left+right)
35 |
36 | for ans in answer:
37 | print("".join(ans))
38 |
39 |
--------------------------------------------------------------------------------
/BOJ/키로거/yerim.py:
--------------------------------------------------------------------------------
1 | # 백준 5397번: 키로거 (Silver 2)
2 | # https://www.acmicpc.net/problem/5397
3 | # 소요 시간 : 60분
4 | from collections import deque
5 |
6 | # IDEA: 커서 기준으로 left, right 분할
7 | # for _ in range(int(input())):
8 | # keylog = deque(input())
9 | # left = deque()
10 | # right = deque()
11 | # for k in keylog:
12 | # if k == '<' and len(left) > 0: # 바 보 ? . . 여기서 초반에 left, right 모두 비어있을 때 걸리지 못해서 <이 자꾸 문자로 인식됨
13 | # right.append(left.pop())
14 |
15 | # elif k == '>' and len(right) > 0:
16 | # left.append(right.popleft())
17 |
18 | # elif k == '-' and len(left) > 0:
19 | # left.pop()
20 |
21 | # else: # 문자인 경우
22 | # left.append(k)
23 |
24 | # print(k, left, right)
25 | # print(''.join(left.extend(right)))
26 |
27 |
28 | # 60 풀이 후 정답 확인 => 거의 다 풀었긴 했네요,,
29 | for _ in range(int(input())):
30 | keylog = deque(input())
31 | left, right = deque(), deque()
32 |
33 | for k in keylog:
34 | if k == "<":
35 | if left:
36 | right.appendleft(left.pop())
37 |
38 | elif k == ">":
39 | if right:
40 | left.append(right.popleft())
41 |
42 | elif k == "-":
43 | if left:
44 | left.pop()
45 |
46 | else: # 문자인 경우
47 | left.append(k)
48 |
49 | left.extend(right)
50 | print("".join(left))
51 |
--------------------------------------------------------------------------------
/CodeTree/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RecoRecoNi/Algorithm-Study/9a1092f08117ce3fa6f1c7b00086d4a850fbe73a/CodeTree/README.md
--------------------------------------------------------------------------------
/Programmers/README.md:
--------------------------------------------------------------------------------
1 | # Programmers - 문제풀이
2 |
--------------------------------------------------------------------------------
/Programmers/k진수에서 소수 개수 구하기/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 40분
5 |
6 | 접근법
7 | - N = 100만 -> O(NlogN) / O(N)
8 | - 네가지 경우를 줬지만 생각해보니 이건 사실상 경우를 나누라는 뜻이 아니라 0 사이에 껴있는 소수를 찾으라는 말임
9 | - 테케 1 문제에서 시간초과가 났음
10 | - 아무리 생각해봐도 시간을 줄일 부분은 소수 확인 부분 밖에 없다고 판단
11 | - 옛날 옛적 소수를 판별하는 데 효율적인 방법이 있었다고 기억이 남 -> 도저히 뭔지 기억 안나서 구글링 했습니다 ..ㅎ -> 바꿨더니 시간초과 해결!
12 |
13 | 회고
14 | - 십진수에서 n진수 변환하는 법을 까먹었네요..ㅎㅎ
15 | - 소수, 진수에 대한 문제들을 몇번 봤던 것 같아서 이번 기회에 까먹은 개념들을 다시 톺아봐야할 것 같음
16 |
17 | """
18 |
19 | # n 진수 변환
20 | def translate(n, k):
21 | num = []
22 | mok = n
23 | while mok!=0: # 몫이 0 이 될때 진수변환 끝
24 | remain = mok % k
25 | mok //= k
26 | num.append(str(remain))
27 | num = ''.join(num[::-1])
28 | return num
29 |
30 | # 소수인지 확인
31 | def check_sosu(num):
32 | for n in range(2, int(num**(1/2)) + 1): # 해당 수의 제곱근까지만 확인해보아도 판별 가능! 원래는 range(2, num) 으로서 O(N)의 코드를 작성했었습니다.
33 | if num % n ==0:
34 | return False
35 | return True
36 |
37 | def solution(n, k):
38 | num = translate(n, k)
39 | num = num.split('0') # 0 을 떨굼
40 | answer = 0
41 |
42 | for n in num:
43 | if n=='' or n=='1':
44 | continue
45 |
46 | if check_sosu(int(n)):
47 | answer += 1
48 |
49 | return answer
50 |
51 |
52 | print(solution(n=437674, k=3)) # 3
53 | print('----------')
54 | print(solution(n=110011, k=10)) # 2
--------------------------------------------------------------------------------
/Programmers/k진수에서 소수 개수 구하기/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023-08-20 13:24
3 |
4 | 1 <= n <= 1,000,000 이므로, 1,000,000을 2진수로 바꿨을 때 길이가 M 이라면
5 | 1 <= M <= 20 -> 그냥 일단 구현하면 되겠다.
6 |
7 | n을 k진수로 바꾼 후 0을 기준으로 split
8 |
9 | 가장 왼쪽, 오른쪽 원소 -> P0, 0P 검사 : 그냥 가장 왼쪽, 오른속 원소가 소수인지
10 | 중간 원소 -> 0P0 검사 : 가운데 원소가 소수인지
11 | 원소가 하나일 경우 -> P 검사 -> 해당 원소가 소수인지
12 |
13 | ===> 결국 0을 split 한 리스트에서 소수 개수를 세면 된다.
14 |
15 | 풀이 완료 : 2023-08-20 13:48 (풀이 시간 : 24분)
16 |
17 | '''
18 | from typing import List
19 |
20 | def de2k(n:int, k: int) -> str: # 10진수(decimal) -> k진수
21 | result = ''
22 |
23 | while n > 0:
24 | result += str(n%k)
25 | n //= k
26 |
27 | return str(result[::-1])
28 |
29 | def isPrimeNumber(num: int) -> bool: # 소수 판별
30 | if num == 1:
31 | return False
32 | else:
33 | for i in range(2, int(num**0.5)+1): # n의 n을 제외한 모든 약수는 [1, sqrt(n)] 내에 존재한다.
34 | if num%i == 0:
35 | return False
36 |
37 | return True
38 |
39 | def solution(n: int, k: int) -> int:
40 | answer: int = 0
41 | kNum: str = de2k(n, k) # k진수로 바꾸기
42 | splitList: List[str] = kNum.split('0') # 0을 기준으로 split
43 |
44 | for num in splitList: # 그 중 소수의 숫자 세기
45 | if num and isPrimeNumber(int(num)):
46 | answer += 1
47 |
48 | return answer
49 |
50 | def main() -> None:
51 | case1 = [437674, 3]
52 | case2 = [110011, 10]
53 |
54 | print(solution(*case1)) # 3
55 | print(solution(*case2)) # 2
56 |
57 | main()
--------------------------------------------------------------------------------
/Programmers/k진수에서 소수 개수 구하기/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 |
5 | - 1 ≤ n ≤ 1,000,000
6 | - 3 ≤ k ≤ 10
7 |
8 |
9 | 1) n을 k진수로 변환
10 | 2) 소수 판별
11 |
12 | <시간복잡도>
13 | O(log_k*n * √n)
14 | """
15 | # n을 k진수로 변환하는 함수
16 | def convert_to_base(n: int, base: int) -> str:
17 | result = '' # k진수로 변환 후 값
18 | while n:
19 | n, remainder = divmod(n, base)
20 | result += str(remainder)
21 | return result[::-1]
22 |
23 | # 소수 판별
24 | def is_prime(number: int) -> bool:
25 | if number <= 1:
26 | return False
27 | i = 2
28 | while i * i <= number:
29 | if number % i == 0:
30 | return False
31 | i += 1
32 | return True
33 |
34 |
35 | def solution(n: int, k: int) -> int:
36 | converted_num = convert_to_base(n, k) # n을 k진수로 변환
37 | cnt = 0 # 조건에 맞는 소수의 개수
38 | # 변환된 수에 대한 조건으로 0이 양쪽 or 오른쪽 or 왼쪽 or 아예 안 붙는 경우에 해당하는 지 확인해야 하기 때문에 0을 기준으로 나눠줌
39 | # -> 0을 기준으로 나눠지는 수는 해당 조건들 중 어느 하나라도 만족함
40 | for segment in converted_num.split('0'):
41 | if not segment: # 빈 문자열
42 | continue
43 | if is_prime(int(segment)): # 소수
44 | cnt += 1
45 | return cnt
46 |
47 | # 테스트 케이스
48 | n1, k1 = 437674, 3
49 | print(solution(n1, k1))
--------------------------------------------------------------------------------
/Programmers/k진수에서 소수 개수 구하기/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | k진수에서 소수 개수 구하기
3 | https://school.programmers.co.kr/learn/courses/30/lessons/92335
4 |
5 | 풀이시간
6 | 24:01 ~ 01:31
7 |
8 | 접근법
9 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 문자열 내장 함수 / n진수 변환 법
10 |
11 | 1. 10진수 to N 진수 변환
12 | 2. 0을 기준으로 split() 진행
13 | 3. split된 데이터를 기준으로 10진수로 변환 후
14 | 3-1. 소수인지 검사하는 함수
15 |
16 | 3. split된 데이터를 기준으로 10진수로 변환 후 을 계속 n진수로 표기된 값을 다시 10진수로 변환해서
17 | 그 값이 소수인지 판단하는지 검사하는 삽질을 시행하여 문제가 풀리지 않는 문제가 있었다.
18 | """
19 |
20 |
21 | def covert_to_notation(n: int, k: int) -> str:
22 | # 거꾸로 계산된 n 진수
23 | rev_output: str = ""
24 |
25 | # n진수 변환
26 | while n > 0:
27 | n, mod = divmod(n, k)
28 | rev_output += str(mod)
29 |
30 | # 뒤집기
31 | return rev_output[::-1]
32 |
33 |
34 | def is_primary_number(target: int):
35 | for idx in range(2, int(target**0.5) + 1):
36 | if target % idx == 0:
37 | print(idx)
38 | return False
39 | return True
40 |
41 |
42 | def solution(n: int, k: int):
43 | answer: int = 0
44 |
45 | # 1. 10진수 to N 진수 변환
46 | notation_output = covert_to_notation(n, k)
47 |
48 | # 2. 0을 기준으로 split() 진행
49 | for primary_num in notation_output.split("0"):
50 | if primary_num not in ["", "1"]:
51 | # print("num :", primary_num)
52 |
53 | # 3. split된 데이터를 기준으로 10진수로 변환 후
54 | # decimal = convert_to_decimal(primary_num, k)
55 | decimal = int(primary_num)
56 |
57 | # 3-1. 소수인지 검사하는 함수
58 | if is_primary_number(decimal):
59 | # print("Decimal :", decimal)
60 | answer += 1
61 |
62 | return answer
63 |
64 |
65 | print(solution(437674, 3)) # 3
66 | print(solution(110011, 10)) # 2
67 |
--------------------------------------------------------------------------------
/Programmers/n진수 게임/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 50 분
5 |
6 | 접근법
7 | - 주어진 변수들의 범위가 매우 적은 편 -> 구현에 집중
8 | - 숫자를 계속 해서 빼는 작업을 해야함 -> 큐를 활용하면 시간복잡도에서 장점이 많을 것이라 판단
9 | - appendleft : 진법 변환할 때 따로 정렬하지 않아도 됨
10 | - popleft : 글자를 빼낼 때 유용
11 |
12 | 회고
13 | - 다른 사람의 풀이를 보니 본인의 코드에서 차례를 초기화하는 과정을 슬라이싱을 잘 활용해서 조금 더 코드를 간결하게 한 것 같음
14 | - 개인적으로도 이 부분을 조금 더 간결하게 하고 싶어 고민했는데 ㅠㅠ 머리만 굴리지말고 조금 더 적어보면서 고민하기
15 |
16 | """
17 |
18 | from collections import deque
19 |
20 | def transform(number, k):
21 | """
22 | number : 변환하고 싶은 10진수
23 | k : 변환하고 싶은 k 진법
24 | """
25 | if number==0:
26 | return deque(['0'])
27 |
28 | replace_num_to_str = {10:'A', 11:'B', 12:'C', 13:'D', 14:'E', 15:'F'}
29 | transformed_number = deque([])
30 |
31 | while number > 0:
32 | m = number % k
33 | number = number // k
34 | transformed_number.appendleft(f'{m}' if m not in replace_num_to_str else replace_num_to_str[m])
35 |
36 | return transformed_number
37 |
38 | def solution(n, t, m, p):
39 | temp_m = 0
40 | answer = ''
41 | num = -1 # 0 부터 시작하기 위해 -1
42 |
43 | while True:
44 | num += 1
45 | transformed_nums = transform(num, n) # n진수로 변환
46 |
47 | while transformed_nums: # 변환된 n진수가 비워질 때 까지 반복
48 | temp_m += 1 # 순서 +1
49 | transformed_num = transformed_nums.popleft() # 숫자 한 차례 제거
50 |
51 | if temp_m == p: # 튜브의 차례가 온다면
52 | answer += transformed_num # 정답에 한 글자 더하기
53 |
54 | if len(answer) == t: # 정답 조건을 만족하면
55 | return answer # 정답을 반환하며 종료
56 |
57 | if temp_m == m: # 한 차례가 끝나고도 n진수가 비워지지 않았다면 다시 차례 초기화하기
58 | temp_m = 0
59 |
--------------------------------------------------------------------------------
/Programmers/n진수 게임/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-03 23:00
3 |
4 | - 10진수를 n진수로 변환하는 함수 정의
5 | - 최대 16진수까지 변환할 수 있으므로 알파벳 F까지 추가하는 로직 구현해야 함
6 | - 문자열의 길이가 (m*t)이 될 때까지 n진수 이어 붙이기
7 | - 튜브 순서가 t번 돌 수 있을 때까지
8 | - 튜브의 순서대로 문자 출력 [::p], 개수 오버하는 경우 t개까지만 출력
9 |
10 | 풀이 완료 : 2023-09-03 23:20 (20분 소요)
11 |
12 | """
13 |
14 |
15 | def de2n(num: int, base: int) -> str:
16 | """
17 | n을 base진수로 변환하는 메서드
18 | 이 떄 base는 최대 16이다.
19 | """
20 | if num == 0:
21 | return "0"
22 |
23 | result = ""
24 |
25 | while num > 0:
26 | rem = num % base
27 | num //= base
28 |
29 | if rem == 15: # 최대 16진수 알파벳 처리 로직
30 | result += "F"
31 | elif rem == 14:
32 | result += "E"
33 | elif rem == 13:
34 | result += "D"
35 | elif rem == 12:
36 | result += "C"
37 | elif rem == 11:
38 | result += "B"
39 | elif rem == 10:
40 | result += "A"
41 | else:
42 | result += str(rem)
43 |
44 | return result[::-1]
45 |
46 |
47 | def solution(n: int, t: int, m: int, p: int) -> str:
48 | """
49 | n : 변환할 진수
50 | t : 미리 구할 숫자의 갯수
51 | m : 게임에 참가하는 인원
52 | p : 튜브의 순서
53 | """
54 | tmp = ""
55 | num = 0
56 | while len(tmp) < (m * t): # 튜브 순서가 t번 돌 수 있을 때까지
57 | tmp += de2n(num, n) # n진수로 변환하여 이어붙임
58 | num += 1
59 | return tmp[p - 1 :: m][:t] # 오버하는 경우 t개까지만 출력
60 |
61 |
62 | def main() -> None:
63 | case1 = [2, 4, 2, 1]
64 | case2 = [16, 16, 2, 1]
65 | case3 = [16, 16, 2, 2]
66 |
67 | print(solution(*case1)) # "0111"
68 | print(solution(*case2)) # "02468ACE11111111"
69 | print(solution(*case3)) # "13579BDF01234567"
70 |
71 |
72 | main()
73 |
--------------------------------------------------------------------------------
/Programmers/게임 맵 최단거리/dohyun .py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 20분
5 |
6 | 접근법
7 | - 그래프 탐색, 최단거리 -> BFS
8 |
9 | 회고
10 | - 기본적인 문제, 행과 열 헷갈려서 버벅거리지말고 차분히 풀자!
11 |
12 | """
13 |
14 | from collections import deque
15 |
16 | def solution(maps):
17 | n, m = len(maps), len(maps[0])
18 | direction = [[0,-1], [0,1], [-1, 0], [1, 0]] # 상하좌우
19 |
20 | queue = deque()
21 | queue.append([0, 0]) # 시작점(0,0)과 종료점(n-1, m-1)이 고정
22 | maps[0][0] += 1
23 |
24 | while queue:
25 | x, y = queue.popleft()
26 |
27 | for dir in direction:
28 | nx, ny = x + dir[0], y + dir[1]
29 |
30 | if (0 <= nx < n) and (0 <= ny < m):
31 | if maps[nx][ny]==1: # 벽이 없으면 지나가기
32 | queue.append([nx, ny])
33 | maps[nx][ny] = maps[x][y] + 1
34 |
35 | return maps[n-1][m-1]-1 if maps[n-1][m-1]>1 else -1
--------------------------------------------------------------------------------
/Programmers/게임 맵 최단거리/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 게임 맵 최단거리
3 | https://school.programmers.co.kr/learn/courses/30/lessons/1844
4 |
5 | 풀이시간
6 | 20:25 ~ 20:37 (12분)
7 |
8 | 문제 조건
9 | Maps = N x M
10 |
11 | 1 <= N <= 100
12 | 1 <= M <= 100
13 |
14 | 시간 복잡도 :
15 | O(N * M) = O(10,000)
16 |
17 | 접근법
18 | 무슨 알고리즘으로 풀이 할 수 있을까? -> BFS
19 |
20 | 전형적인 BFS 알고리즘을 사용한 풀이
21 | """
22 |
23 | from collections import deque
24 |
25 | def bfs(start,maps):
26 | # BFS 탐색을 위한 deque
27 | queue = deque([start])
28 |
29 | # 하,상,좌,우
30 | directions = ((0,1),(0,-1),(-1,0),(1,0))
31 |
32 | # 방문 노드 기억하기
33 | visited = [[False]*len(maps[0]) for _ in range(len(maps))]
34 | visited[start[0]][start[1]] = True
35 |
36 | # BFS 탐색
37 | while queue:
38 | x,y = queue.popleft()
39 |
40 | # 4방향으로 검사
41 | for dx,dy in directions:
42 | nx,ny = x + dx, y + dy
43 | # 맵 밖으로 나가지 않고
44 | if 0 <= nx < len(visited) and 0 <= ny < len(visited[0]):
45 | # 벽이 아니고 방문한 적이 없다면
46 | if maps[nx][ny] > 0 and not visited[nx][ny]:
47 | # 다음 방문 노드 추가 및 방문 처리와 현재까지 걸린 시간 업데이트
48 | queue.append((nx,ny)) # 다음 방문 노드 추가
49 | maps[nx][ny] += maps[x][y] # 현재까지 걸린 시간
50 | visited[nx][ny] = True # 방문 처리
51 |
52 | # 예외처리 : n,m 진영이 1이라면 방문할 수 없는 것
53 | return maps[-1][-1] if maps[-1][-1] > 1 else -1
54 |
55 |
56 | def solution(maps):
57 | return bfs((0,0),maps)
58 |
59 | case1 = [[1,0,1,1,1],[1,0,1,0,1],[1,0,1,1,1],[1,1,1,0,1],[0,0,0,0,1]] # 11
60 | case2 = [[1,0,1,1,1],[1,0,1,0,1],[1,0,1,1,1],[1,1,1,0,0],[0,0,0,0,1]] # -1
61 |
62 | print(solution(case1))
63 | print(solution(case2))
--------------------------------------------------------------------------------
/Programmers/네트워크/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간
5 |
6 | 접근법
7 | - N = 200 -> 시간 복잡도 크게 고려하지 않아도 될 것 같음 (각 배열을 고려해 N^2 해도 충분)
8 | - 모든 컴퓨터를 탐색하여 연결 정보를 찾아야 함
9 | - 그래프 탐색 알고리즘(DFS/BFS)을 사용해야겠다라고 판단
10 | - 제한 시간을 초과해서 답지를 봤습니다 .. ㅎ
11 |
12 | 회고
13 | - DFS/BFS 유형은 매우 중요한데도 거의 손도 못대겠음 ㅠㅠ 해당 유형 문제 많이 풀어보기
14 |
15 | """
16 |
17 | def solution(n, computers):
18 | def dfs(node):
19 | visited[node] = True # 현재 노드 방문 표시
20 | for neighbor in range(n): # 모든 노드 반복
21 | if computers[node][neighbor] == 1 and not visited[neighbor]: # 현재 노드와 연결되어 있고 and 방문하지 않은 노드
22 | dfs(neighbor)
23 |
24 | visited = [False] * n
25 | networks = 0
26 |
27 | for i in range(n):
28 | if not visited[i]: # 방문하지 않은 노드에 대해 dfs 실시
29 | dfs(i)
30 | networks += 1
31 |
32 | return networks
33 |
34 | print(solution(n=3, computers=[[1, 1, 0], [1, 1, 0], [0, 0, 1]])) # 2
35 | print('----------')
36 | print(solution(n=3, computers=[[1, 1, 0], [1, 1, 1], [0, 1, 1]])) # 1
--------------------------------------------------------------------------------
/Programmers/단어 변환/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 30분
5 |
6 | 접근법
7 | - 하나의 단어는 접근하면 다시 사용하지 않음 (왜? 다시 접근할거면 변환을 할 필요가 없으니까, 무한 루프도는거)
8 | - 한번 방문한 곳은 다시 안감 + 완전탐색 + 최소 count -> BFS
9 | - "한 번에 한 개의 알파벳만 바꿀 수 있다" 조건만 잘 처리하자!
10 | - 단어의 길이가 최대 10이므로 그냥 단순 순회하며 확인해보면 될 듯함
11 |
12 | 회고
13 | - 한 개의 알파벳만 바꿀 수 있다 -> "BCB" 와 같은 단어가 오면 "ACA" 로도 바꿀 수 있다
14 | - 이렇게 해석을 잘 못해서 시간이 좀 오래 걸렸음 ㅠ ㅠ
15 |
16 | """
17 |
18 | from collections import deque
19 |
20 | def solution(begin, target, words):
21 | if target not in words: # 정답이 없으면 0 반환
22 | return 0
23 |
24 | def check_word(word1, word2): # 한 개의 알파벳만 바꿔도 되는지 여부를 반환
25 | cnt = 0
26 | for w1, w2 in zip(word1, word2):
27 | if w1 != w2:
28 | cnt += 1
29 | if cnt > 1:
30 | return False
31 | return True
32 |
33 | words.append(begin)
34 | visit = [-1] * (len(words))
35 | queue = deque([len(words)-1])
36 | visit[len(words)-1] = 0
37 |
38 | target_idx = words.index(target)
39 |
40 | while queue: # BFS
41 | node = queue.popleft()
42 |
43 | for i in range(len(words)):
44 | if visit[i] == -1: # 방문하지 않은 단어에만 접근
45 | if check_word(words[node], words[i]): # 한개의 알파벳 조건 만족하면
46 | queue.append(i) # 해당 단어도 큐에 추가
47 | visit[i] = visit[node] + 1
48 | elif i==target_idx: # 이미 방문한적이 있으며 target 에 도달했다면 정답 반환
49 | return visit[target_idx]
--------------------------------------------------------------------------------
/Programmers/더 맵게/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분
5 |
6 | 접근법
7 | - 스코빌의 길이는 최대 백만 -> O(N) 에 가깝게 해결해야함
8 | - 최대, 최소를 넣었다 뺐다 -> 힙을 이용하면 되겠다!
9 |
10 | 회고
11 | - 수민님이 저번에 보여주신 힙이 기억에 나서 풀 수 있었습니다!!!
12 |
13 | """
14 | import heapq
15 |
16 | def solution(scoville, K):
17 | heapq.heapify(scoville)
18 | answer = 0
19 |
20 | while True:
21 | not_spicy_one = heapq.heappop(scoville)
22 |
23 | if not_spicy_one >= K:
24 | return answer
25 | elif not scoville:
26 | return -1
27 | else:
28 | not_spicy_two = heapq.heappop(scoville)
29 | new_one = not_spicy_one + not_spicy_two * 2
30 | heapq.heappush(scoville, new_one)
31 | answer += 1
--------------------------------------------------------------------------------
/Programmers/더 맵게/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-05 13:10
3 |
4 | #### 제한 조건
5 | - scovile의 길이 <= 1,000,000 이므로 O(NlogN) 이하의 알고리즘을 설계해야 함.
6 |
7 | #### 풀이
8 | - 계속 스코빌 지수가 가장 낮은 2개를 알아야 하므로 항상 오름차순으로 정렬 되어있어야 한다.
9 | - min heap을 활용해서 root(min)원소가 K보다 작으면 다음 원소를 pop해서 섞기
10 | - heap의 원소가 하나밖에 없는데 해당 원소가 K보다 작은 경우 -1 반환
11 | - min heap으로 변환(O(N)) + 모든 경우 순회할 경우 O(N) * heappush O(logN) => O(NlogN) 설계 가능
12 |
13 | 풀이 완료 : 2023-09-05 13:20(10분 소요)
14 | """
15 |
16 | from typing import List
17 | from heapq import heappush, heappop, heapify
18 |
19 |
20 | def solution(scoville: List[int], K: int) -> int:
21 | """
22 | 모든 요리의 스코빌 지수가 k를 넘을 때까지 최소 몇 번 섞어야 하는지를 반환한다.
23 | 모두 k를 넘길 수 없을 경우 -1을 반환한다.
24 | """
25 | heapify(scoville) # 기존 리스트를 min heap으로 변환
26 | cnt = 0
27 | while scoville[0] < K: # root(min)가 K를 넘을 때까지 반복
28 | if len(scoville) < 2: # root가 K보다 작은데 섞을 다음 요리가 없는 경우
29 | return -1
30 |
31 | one, two = heappop(scoville), heappop(scoville) # 가장 안 매운 두 개 뽑아서
32 | heappush(scoville, one + two * 2) # 섞기
33 | cnt += 1
34 | return cnt
35 |
36 |
37 | def main() -> None:
38 | case1 = [[1, 2, 3, 9, 10, 12], 7]
39 |
40 | print(solution(*case1)) # 2
41 |
42 |
43 | main()
44 |
--------------------------------------------------------------------------------
/Programmers/더 맵게/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 더 맵게
3 | https://school.programmers.co.kr/learn/courses/30/lessons/42626?language=python3
4 |
5 | 풀이시간
6 | 21:06 ~ 21:32(26분)
7 |
8 | 문제 조건
9 | 2 <= len(scoville) = L <= 1,000,000
10 | 0 <= K <= 1,000,000,000
11 |
12 | 시간 복잡도 :
13 | O(L + (L-1) * 2 * logL) = O(1,000,000 + 999,999 * 2 * 1000) = O(2,000,998,000)
14 | O(LlogL)
15 |
16 |
17 | 접근법
18 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 힙
19 | 1. 최소힙을 구현하고 O(N)
20 | 2. 힙의 root가 K 이상인지 확인하고 맞다면 섞은 횟수 반환
21 | 3. 음식의 갯수가 2이상이면 스코빌 지수 섞기 연산 진행
22 | - 스코빌 지수 섞기 연산 => 섞은 음식의 스코빌 지수 = 가장 맵지 않은 음식의 스코빌 지수 + (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)
23 | 4. 음식이 하나만 있을 경우 불가능하기 때문에 -1 반환
24 | """
25 | from typing import List
26 |
27 | import heapq
28 |
29 |
30 | def solution(scoville: List, K: int):
31 | count: int = 0
32 |
33 | # 최소 힙 정렬
34 | heapq.heapify(scoville)
35 |
36 | while scoville:
37 | # 가장 작은 값이 K 이상일 경우 return
38 | if scoville[0] >= K:
39 | return count
40 |
41 | # 음식이 2개 이상인 경우 힙 연산
42 | if len(scoville) >= 2:
43 | heapq.heappush(
44 | scoville,
45 | heapq.heappop(scoville) + heapq.heappop(scoville) * 2, # 스코빌 지수 섞기 연산
46 | )
47 | # 음식이 하나만 있는 경우 불가능
48 | else:
49 | return -1
50 |
51 | count += 1
52 |
53 |
54 | case1 = [[1, 2, 3, 9, 10, 12], 7]
55 | case2 = [[2, 3], 7]
56 | case3 = [[1, 2, 3, 9, 10, 12], 100000000]
57 | print(solution(*case1))
58 | print(solution(*case2))
59 | print(solution(*case3))
60 |
--------------------------------------------------------------------------------
/Programmers/두 원 사이의 정수 쌍/dohyun.py:
--------------------------------------------------------------------------------
1 | def solution(r1, r2):
2 | """
3 |
4 | 풀이시간
5 | - 약 1시간
6 |
7 | 접근법
8 | - 모든 정수 좌표 배열을 구해서 거리를 구하면 될까? -> 주어진 반지름의 길이가 매우 크기 때문에 시간 복잡도로 인한 오류가 날 가능성이 크다!
9 | - 이중 반복문으로 반지름 조건만 만족하면 += 1 을 할까? -> 이것도 시간 복잡도가...
10 | - 최소 하나의 반복문을 쓰는건 불가피하다고 생각, 남은 하나의 좌표는 조건에 맞도록 유동적으로 변경해야겠다
11 | - 정수의 개수이기 때문에 count 방식으로 해결해나가기 보다는 length 느낌으로 풀어 나갈 수 있겠다!
12 |
13 | 회고
14 | - 이것저것 많은 방식을 시도하느라 생각보다 오래걸렸는데 엄청 어려운 문제는 아니었던것 같다.
15 | - ~~ 방식은 시간복잡도 문제가 생길걸 알면서도 시도해봤는데 안좋은 습관인 것 같음. 문제 접근법에 대해 고민하는 시간 늘리기
16 |
17 | """
18 |
19 | answer = 0
20 | max_y = 0 ; min_y = 0
21 |
22 | for x in range(r2, 0, -1): # 오른쪽 x 부터 카운트 시작
23 | while (x**2 + min_y**2) < r1**2: # 작은 원 내부에 들어오면 최소 y 좌표를 상승
24 | min_y += 1
25 | while (x**2 + (max_y+1)**2) <= r2**2: # 큰 원 내부에 있는 한 최대 y 좌표를 상승
26 | max_y += 1
27 | answer += (max_y - min_y) + 1 # 길이를 더해주는 것이 정수의 개수를 더하는 것과 같음
28 |
29 | return answer * 4 # 1사분면의 개수만 구하면 4사분면의 개수는 대칭성
30 |
--------------------------------------------------------------------------------
/Programmers/두 원 사이의 정수 쌍/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 40분
3 |
4 |
5 | 1 ≤ r1 < r2 ≤ 1,000,000 -> O(N)으로 설계
6 |
7 |
8 | 1. 1사분면에서 x좌표를 기준으로 정수 좌표의 개수를 구한다.
9 | - 피타고라스 정리를 사용
10 | - 처음에 이 부분에서 애를 먹었는데, "각 원 위의 점도 포함하여 셉니다."라는 조건이 있기 때문에 r1=5, x=3일 때와 같이 원 위에 좌표가 생기는 경우를 고려해야 한다.
11 | 2. 위에서 구한 개수에 x 4를 해줘 전체 개수를 구한다.
12 | 3. x축과 y축에 찍히는 좌표의 개수를 추가해준다.
13 |
14 |
15 | - https://sasca37.tistory.com/320
16 | """
17 |
18 | import math
19 | def solution(r1, r2):
20 | answer = 0 # 두 원 사이의 공간에 x좌표와 y좌표가 모두 정수인 점의 개수
21 |
22 | for x in range(1, r2): # x좌표가 1부터 r2-1까지
23 | if x < r1: # x좌표가 r1보다 작다면 r2 반지름의 원에서 생기는 좌표의 개수에서 r1 반지름의 원에 생기는 좌표의 개수를 빼줌
24 | answer += count(x, r2, "large") - count(x, r1, "small")
25 | else: # x좌표가 r1과 같거나 더 크다면, r2 반지름의 원에서 생기는 좌표의 개수만 구하면 된다.
26 | answer += count(x, r2, "large")
27 | answer *= 4
28 | answer += 4 * (r2 - r1 + 1) # x축 및 y축에 찍히는 좌표의 개수
29 |
30 | return answer
31 |
32 |
33 | def count(a, c, kind): # 밑변이 a, 빗변이 c인 삼각형을 이용해, 반지름이 c인 원의 x좌표에서 생기는 정수 좌표의 개수
34 | b = math.sqrt(c**2 - a**2)
35 | cnt = int(b)
36 | if kind == "small" and b == cnt: # 작은 원이 정수 좌표와 일치하는 지점이 있을 때, 원 위의 점도 포함시켜야 하기 때문에
37 | return cnt-1 # 개수를 하나 빼준다(ex. r1=5, x=3)
38 | return cnt
39 |
40 | """
41 | 테스트 1 〉 통과 (0.02ms, 10.1MB)
42 | 테스트 2 〉 통과 (0.04ms, 10.2MB)
43 | 테스트 3 〉 통과 (0.06ms, 10.3MB)
44 | 테스트 4 〉 통과 (2.84ms, 10.4MB)
45 | 테스트 5 〉 통과 (0.84ms, 10.3MB)
46 | 테스트 6 〉 통과 (4.40ms, 10.3MB)
47 | 테스트 7 〉 통과 (701.70ms, 10.2MB)
48 | 테스트 8 〉 통과 (1411.76ms, 10.2MB)
49 | 테스트 9 〉 통과 (666.18ms, 10.2MB)
50 | 테스트 10 〉 통과 (979.39ms, 10.2MB)
51 | """
52 |
--------------------------------------------------------------------------------
/Programmers/두 원 사이의 정수 쌍/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 두 원 사이의 정수 쌍
3 | https://school.programmers.co.kr/learn/courses/30/lessons/181187
4 |
5 |
6 | 접근법
7 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 단순 구현
8 |
9 |
10 | 1. r2에 포함되는 정수 좌표 - r1에 포함되는 정수 좌표
11 | (r2 원의 내접사각형 + r2의 x축과 y축에 접하는 점 4개) - (r1 원의 내접 사각형)
12 | # 5**2 - 3**2 + 4
13 | # (r2지름 - 1)^2 - (r1지름 - 1)^2 + 4
14 | 1) r1 < r2
15 | 2) r1 >= 1
16 |
17 | 2. 피타고라스를 사용하여 직접 구해보자
18 | 풀이참조 : https://school.programmers.co.kr/questions/47373
19 | x^2 + y^2 = r^2
20 | r^2 - x^2 = y^2
21 | y = sqrt(r^2 - x^2)
22 |
23 | """
24 | from math import floor, ceil, sqrt
25 |
26 |
27 | def solution(r1, r2):
28 | answer = 0
29 | min_y, max_y = r1, r2
30 |
31 | # 제1 사분면의 정수 쌍의 갯수를 구하고 4배하기
32 | ## x값을 고정하고 y 값을 찾으며 연산
33 |
34 | # 0부터 시작하였을 때는 4배를 하는 순간 중복된 점을 포함하기 때문에 제외하고 1부터 x 탐색
35 | for x in range(1, r2 + 1):
36 | # 최대 y 값의 범위는 버림 연산을 통해 구한다.
37 | max_y = floor(sqrt(r2**2 - x**2)) # 버림
38 |
39 | # 최소 y 값의 범위는 r1보다 커야하기에 올림 연산을 통해 구한다.
40 | min_y = 0 if x > r1 else ceil(sqrt(r1**2 - x**2)) # 올림
41 | answer += max_y - min_y + 1 # max_y ~ min_y 범위의 점의 갯수는 max_y-min_y+1
42 |
43 | return answer * 4 # 총 4사분면 전체에 대한 갯수
44 |
45 |
46 | # 1 Case #1
47 | r1, r2 = 2, 3
48 | print(solution(r1, r2))
49 |
50 | # 테스트 1 〉 통과 (0.02ms, 10MB)
51 | # 테스트 2 〉 통과 (0.04ms, 10.1MB)
52 | # 테스트 3 〉 통과 (0.05ms, 10.1MB)
53 | # 테스트 4 〉 통과 (1.14ms, 10.2MB)
54 | # 테스트 5 〉 통과 (1.19ms, 10.2MB)
55 | # 테스트 6 〉 통과 (2.54ms, 9.94MB)
56 | # 테스트 7 〉 통과 (623.90ms, 10.1MB)
57 | # 테스트 8 〉 통과 (957.51ms, 10.1MB)
58 | # 테스트 9 〉 통과 (540.68ms, 10MB)
59 | # 테스트 10 〉 통과 (877.39ms, 10.2MB)
60 |
--------------------------------------------------------------------------------
/Programmers/두 원 사이의 정수 쌍/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간 : 1시간
3 | 시간 초과가 계속 떠서 답안 확인
4 |
5 | [접근 방법]
6 | - 큰 원 안의 점 개수 - 작은 원 안의 점 개수
7 | - 사분면에서 다 대칭이므로 한 면에서만 구하고 4를 곱해주면 됨
8 | - 원의 반지름과 거리를 바탕으로 높이 계산
9 | - 작은 원의 높이가 0이면 +1을 해주기 위해 반올림 후 +1 하는 방식 사용
10 |
11 | [참고한 블로그](https://safetymo.tistory.com/13)
12 | """
13 |
14 | import math
15 |
16 | def solution(r1, r2):
17 | answer = 0
18 | for i in range(1, r2 + 1):
19 | high1 = math.sqrt(r2 ** 2 - i ** 2)
20 | high2 = 0 if i > r1 else math.sqrt(r1 ** 2 - i ** 2)
21 |
22 | answer += math.floor(high1) - math.ceil(high2) + 1 # floor : 내림 / ceil : 올림
23 |
24 | return answer * 4
25 |
26 | # # 초기 접근 방법 (시간 초과)
27 | # for x in range(0, r2 + 1):
28 | # for y in range(1, r2 + 1):
29 | # r_squared = x ** 2 + y ** 2
30 | # if (r_squared >= r1 ** 2) and (r_squared <= r2 ** 2):
31 | # answer += 1
32 |
33 | # return answer * 4
--------------------------------------------------------------------------------
/Programmers/뒤에 있는 큰 수 찾기/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - 구현 자체는 쉬워보이지만 N 이 최대 100만 -> O(N) 근처의 복잡도로 해결해야함
8 | - result 와 numbers 의 길이가 같음 -> 뭔가 여러번 복잡한 순회보다는 인덱싱을 활용하면 최소한의 순회가 될 것 같음
9 | - 두 세 문제에서 시간초과 문제를 해결하지 못해서 답지 참조 ㅠ_ㅠ
10 |
11 | - 답지 풀이
12 | - 인덱싱으로 접근 자체는 비슷했던 것 같음
13 | - 스택을 활용하여 시간여행(?)을 가능케했음
14 |
15 | 회고
16 | - 스택을 활용하여 시간여행(?) 한 것이 인상적인데 숙지한다면 꽤나 쓰게될 일이 있을 것 같다!!
17 |
18 | """
19 |
20 | def solution(numbers):
21 | answer = [-1] * len(numbers)
22 | stack = []
23 |
24 | for i, num in enumerate(numbers):
25 | while stack and numbers[stack[-1]] < num: # 스택이 비어있지 않고, 현재 숫자가 스택의 맨 위 숫자보다 큰 경우 (예전 인덱스에 위치한 number 보다 큰 경우)
26 | answer[stack.pop()] = num # 예전 인덱스에 해당하는 answer 의 값을 현재 값으로 바꿔줌
27 | stack.append(i) # 현재 숫자의 인덱스를 스택에 추가
28 |
29 | return answer
--------------------------------------------------------------------------------
/Programmers/뒤에 있는 큰 수 찾기/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 뒤에 있는 큰 수 찾기
3 | https://school.programmers.co.kr/learn/courses/30/lessons/154539
4 |
5 | 풀이시간
6 | 11 :42~48 13:38 ~ 14:10 (풀이 포기)
7 |
8 | 풀이 참고 : https://maximum-curry30.tistory.com/515
9 |
10 | 문제 조건
11 | 4 <= len(numbers) : N <= 1,000,000
12 |
13 | 시간 복잡도 :
14 | O(N)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> stack
18 |
19 | - 오큰수 처리 알고리즘이 존재
20 | - Stack을 사용하여 가장 위의 원소에 해당하는 값보다 현재 검사하는 원소의 값이 작을 때 해당 수가 오 큰수이다.
21 | """
22 |
23 | def solution_fail(numbers):
24 | answer = []
25 | N = len(numbers)
26 |
27 | for i in range(N):
28 | flag = True
29 | for j in range(i+1, N):
30 | if numbers[j] > numbers[i]:
31 | answer.append(numbers[j])
32 | flag = False
33 | break
34 |
35 | if flag:
36 | answer.append(-1)
37 |
38 | return answer
39 |
40 | def solution(numbers):
41 | answer = [-1] * len(numbers)
42 |
43 | # 현재 처리 중인 원소들의 인덱스를 저장하기 위한 스택
44 | stack = []
45 |
46 | for idx, num in enumerate(numbers):
47 | # 스택의 가장 위 원소에 해당하는 값이 현재 원소보다 작을 때, 스택에서 원소를 꺼내오면서 오 큰수 찾기
48 | while stack and numbers[stack[-1]] < num:
49 | answer[stack.pop()] = num
50 |
51 | # 스택의 가장 위 원소보다 크거나 같다면, 스택에 현재 인덱스를 추가
52 | stack.append(idx)
53 | return answer
54 |
55 | case1 = [2, 3, 3, 5]
56 | case2 = [9, 1, 5, 3, 6, 2]
57 | case3 = [10,9,8,7,6,5,4,3,2,1]
58 |
59 | print(solution(case1)) # [3, 5, 5, -1]
60 | print(solution(case2)) # [-1, 5, 6, 6, -1, -1]
61 | print(solution(case3)) # [-1, 5, 6, 6, -1, -1]
--------------------------------------------------------------------------------
/Programmers/등굣길/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-10-03 18:03
3 |
4 | #### 제한 사항
5 | - m과 n은 100이하이지만, 물에 잠긴 지역이 10개 이하이므로 완전탐색으로 모든 경우를 고려하기는 어렵다.
6 |
7 | #### 풀이
8 | - 학창시절 배운 최단경로 탐색 적용
9 | - 오른쪽 혹은 아래로만 갈 수 있다.
10 | - 점화식은 `dp[r][c] = dp[r-1][c] + dp[c][r-1]`
11 | - r과 c가 0일 때를 생각해줘야 한다. 0인 좌표는 제외하고 한 방향에서만 온다.
12 | - 물이 잠긴 지역은 set으로 관리해서 좌표 검사 후 0에서 업데이트를 하지 않으면 자연스레 피해서 가는 것!
13 | - 후.... m이 가로고 n이 세로였다.. 출제자 줄빠따 마렵네
14 | - 후.... puddles도 마찬가지다 주의하자
15 |
16 | 풀이 완료 : 2023-10-03 18:33 (풀이 시간 : 30분 소요)
17 | """
18 | from typing import List
19 |
20 |
21 | def solution(m: int, n: int, puddles: List[List[int]]) -> int:
22 | """
23 | n이 row, m이 col임
24 | """
25 | matrix = [[0 for _ in range(m)] for _ in range(n)]
26 |
27 | # 2차원 리스트 to set
28 | puddles_set = {(row - 1, col - 1) for col, row in puddles}
29 | matrix[0][0] = 1
30 |
31 | for r in range(n):
32 | for c in range(m):
33 | # 물에 잠긴 지역은 갈 수 없다.
34 | if (r, c) in puddles_set or (r == 0 and c == 0):
35 | continue
36 |
37 | # 점화식 : 오른쪽 혹은 아래로만 이동할 수 있다.
38 | if r == 0: # r과 c가 각각 0일 때 예외 처리 : 한 방향에서만 온다
39 | matrix[r][c] += matrix[r][c - 1]
40 | elif c == 0:
41 | matrix[r][c] += matrix[r - 1][c]
42 | else:
43 | matrix[r][c] += matrix[r][c - 1] + matrix[r - 1][c]
44 |
45 | matrix[r][c] %= 1000000007 # dp 최적화
46 |
47 | return matrix[-1][-1] % 1000000007
48 |
49 |
50 | def main() -> None:
51 | case1 = [4, 3, [[2, 2]]]
52 | case2 = [4, 3, [[2, 2], [2, 3]]]
53 | case3 = [4, 3, [[2, 2], [3, 1]]]
54 |
55 | print(solution(*case1)) # 4
56 | print(solution(*case2)) # 3
57 | print(solution(*case3)) # 1
58 |
59 |
60 | main()
61 |
--------------------------------------------------------------------------------
/Programmers/등굣길/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 등굣길
3 | https://school.programmers.co.kr/learn/courses/30/lessons/42898
4 |
5 | 풀이시간
6 | 22:59 ~ 23:59 (풀이참조)
7 | https://dev-note-97.tistory.com/141
8 |
9 | 문제 조건
10 | 1 <= N,M <= 100
11 | 0 <= len(puddles) <= 10
12 |
13 | 시간 복잡도 :
14 | O(100 * 100)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> DP
18 |
19 | 처음에는 BFS/DFS로 접근하며 최단 거리를 가지는 경우를 DP 테이블로 만들려고 시도하였습니다.
20 | 하지만 BFS/DFS 경로 탐색은 최단 거리 탐색을 위한 알고리즘이라 해당 경로로 몇개의 선이 지나갔는지 표현 할 수 없었습니다.
21 | 결국 한 시간은 넘겨 풀이를 참고하고 다시 풀이를 진행하였습니다.
22 |
23 | DP 테이블을 만들기 위해서 DP 테이블의 각 원소가 의미하는 바를 현재 칸을 지나는 경로의 개수로 정의하고 점화식을 세웠습니다.
24 |
25 | 점화식 : dp[y][x] = dp[y-1][x] + dp[y][x-1] (왼쪽 + 오른쪽 경로를 지나는 개수)
26 |
27 | 이렇게 했을 때 범위를 벗어나는 경우에 대한 고려를 해보았지만 row를 기준으로 왼쪽 상단부터 값을 업데이트 하기 때문에 범위를 넘어가는
28 | 경우에는 해당 범위의 값이 0으로 초기화되어있습니다.
29 | """
30 | def solution(m, n, puddles):
31 | # Table 생성
32 | dp = [[0] * (m+1) for _ in range(n+1)]
33 |
34 | # puddles 좌표 집합으로 자료형 변환
35 | puddles = set(map(tuple, puddles))
36 |
37 | # start point
38 | dp[1][1] = 1
39 |
40 | # 모든 테이블에 대해서
41 | for x in range(1, m + 1):
42 | for y in range(1, n + 1):
43 | # 초기값 skip
44 | if x == 1 and y == 1:
45 | continue
46 |
47 | # puddle 값이 아닌 경우 점화식 유도
48 | if (x, y) not in puddles:
49 | # 점화식 : dp[y][x] = dp[y-1][x] + dp[y][x-1] (왼쪽 + 오른쪽 경로를 지나는 개수)
50 | dp[y][x] = (dp[y-1][x] + dp[y][x-1]) % (10**9 + 7)
51 |
52 | return dp[-1][-1]
53 |
54 | case1 = (4, 3, [[2, 2]])
55 | print(solution(*case1)) # 4
--------------------------------------------------------------------------------
/Programmers/땅따먹기/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - N 10만 이하 -> O(NlogN)
8 | - 그리디? -> 현재 행에서 최대라고 해서 합이 최대가 되는게 아니기 때문에 X
9 | - 열은 왜 4개로 고정? -> 각 열별로 결과를 저장해놓으면 될듯
10 | - 현재 열을 제외한 이전 행의 최대값과 현재 열의 값을 계속해서 더해가며 갱신하면 될듯
11 |
12 | 회고
13 | - 로직은 같은데 1차원 배열로만 해결하려다가 사고가 멈춰버린듯함 ㅠㅠ
14 | - DP테이블은 2차원 배열도 가능하다는거 꼭 인지하기
15 |
16 | """
17 |
18 | def solution(land):
19 | N = len(land)
20 |
21 | dp = [[0] * 4 for _ in range(N)] # DP 테이블
22 | dp[0] = land[0]
23 |
24 | for i in range(1, N):
25 | for j in range(4):
26 | dp[i][j] = land[i][j] + max(dp[i-1][:j] + dp[i-1][j+1:]) # 이전 행에서 같은 열을 제외한 나머지 열 중에서 최대 점수 더함
27 |
28 | return max(dp[-1]) # 최대 점수 반환
--------------------------------------------------------------------------------
/Programmers/땅따먹기/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-19 10:21
3 |
4 | #### 제한 사항
5 | - 행의 개수 N : 100,000이하 자연수
6 | - 열의 개수 : 4
7 | - O(NlogN) 이하 알고리즘을 설계해야 한다.
8 |
9 | #### 풀이
10 | - 점수는 100 이하의 "자연수" 이므로 최대값을 더해주는 방식으로 설계하면 될 것 같다.
11 | - i행 에서 i-1행 중 같은 열을 제외하고 최대값으로 누적합을 구하기
12 | - 시간복잡도 : 최대 연산 N(100,000) * 4(i열) * 4(i-1열) = 1,600,000
13 |
14 | 풀이 완료 : 2023-09-19 10:31 (풀이 시간 : 10분 소요)
15 | """
16 | from typing import List
17 |
18 |
19 | def solution(land: List[List[int]]) -> int:
20 | """
21 | i번째 행의 각 열에 i-1번째 행의 해당 열을 제외한 나머지 열들의 최댓값을 누적합한다.
22 | """
23 | for i in range(1, len(land)):
24 | for j in range(4):
25 | land[i][j] += max([land[i - 1][k] for k in range(4) if k != j])
26 |
27 | return max(land[-1])
28 |
29 |
30 | def main() -> None:
31 | case1 = [[1, 2, 3, 5], [5, 6, 7, 8], [4, 3, 2, 1]]
32 |
33 | print(solution(case1)) # 16
34 |
35 |
36 | main()
37 |
--------------------------------------------------------------------------------
/Programmers/땅따먹기/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 10분
3 |
4 |
5 | land: N x 4행으로 이루어진 2차원 배열
6 | - N : 100,000 이하의 자연수
7 | - 열의 개수: 4개
8 | - 점수: 100이하의 자연수
9 |
10 |
11 | 한 행씩 내려올 때, 같은 열을 연속해서 밟을 수 없음
12 | land[i][j]를 land[i][j]를 밟을 때 얻을 수 있는 점수의 최댓값으로 바꿔주기
13 |
14 | <시간 복잡도>
15 | O(N): 총 3번의 for문을 돈다고 해도, 연산횟수는 (n-1) * 4 * 4로 최악의 경우 약 1,600,000임으로 시간내에 통과가 가능하다
16 | """
17 | from typing import List
18 |
19 |
20 | def solution(land: List[List[int]]):
21 | """
22 | land: N x 4행으로 이루어진 2차원 배열
23 | """
24 | n = len(land)
25 | for i in range(1, n):
26 | for j in range(4):
27 | prev = 0 # 이전 행에서 얻을 수 있는 점수의 최댓값
28 | for k in range(4):
29 | if j != k: # 이전 행과 같은 열을 밟을 수 없음
30 | if land[i-1][k] > prev:
31 | prev = land[i-1][k]
32 | land[i][j] += prev
33 |
34 | return max(land[-1])
35 |
36 | """
37 | 정확성 테스트
38 | 테스트 1 〉 통과 (4.12ms, 10.4MB)
39 | 테스트 2 〉 통과 (3.11ms, 10.5MB)
40 | 테스트 3 〉 통과 (3.11ms, 10.5MB)
41 | 테스트 4 〉 통과 (3.25ms, 10.4MB)
42 | 테스트 5 〉 통과 (3.28ms, 10.5MB)
43 | 테스트 6 〉 통과 (3.13ms, 10.2MB)
44 | 테스트 7 〉 통과 (3.07ms, 10.3MB)
45 | 테스트 8 〉 통과 (3.12ms, 10.4MB)
46 | 테스트 9 〉 통과 (3.10ms, 10.4MB)
47 | 테스트 10 〉 통과 (3.13ms, 10.5MB)
48 | 테스트 11 〉 통과 (3.08ms, 10.3MB)
49 | 테스트 12 〉 통과 (3.09ms, 10.3MB)
50 | 테스트 13 〉 통과 (3.10ms, 10.1MB)
51 | 테스트 14 〉 통과 (3.28ms, 10.4MB)
52 | 테스트 15 〉 통과 (3.19ms, 10.3MB)
53 | 테스트 16 〉 통과 (3.09ms, 10.4MB)
54 | 테스트 17 〉 통과 (3.08ms, 10.3MB)
55 | 테스트 18 〉 통과 (3.07ms, 10.4MB)
56 |
57 | 효율성 테스트
58 | 테스트 1 〉 통과 (314.70ms, 32.4MB)
59 | 테스트 2 〉 통과 (317.33ms, 32.2MB)
60 | 테스트 3 〉 통과 (316.18ms, 32.5MB)
61 | 테스트 4 〉 통과 (316.38ms, 32.3MB)
62 | """
--------------------------------------------------------------------------------
/Programmers/땅따먹기/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 땅따먹기
3 | https://school.programmers.co.kr/learn/courses/30/lessons/12913
4 |
5 | 풀이시간
6 | 19:09 ~ 19:18 / 19:40 ~ 20:20 (49분)
7 |
8 | 문제 조건
9 | 행의 개수(N) : 100,000 이하의 자연수
10 | 열의 개수 : 4개
11 | 점수 : 최대 100이하의 자연수
12 |
13 | 시간 복잡도 :
14 | O(N * (4 * 3 * 3) + 4) = O(N)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> DP
18 |
19 | - 현재 행의 자기 열의 값에 이전 행의 자신의 열을 제외한 나머지 열들의 최대 값을 현재 열에 더해주기
20 |
21 | dp 테이블은 현재 행의 자신의 열중 최댓값을 기록
22 | """
23 | def solution(land):
24 | for row_idx in range(1, len(land)):
25 | land[row_idx][0] += max(land[row_idx - 1][1:])
26 | land[row_idx][1] += max(land[row_idx - 1][:1] + land[row_idx-1][2:])
27 | land[row_idx][2] += max(land[row_idx - 1][:2] + land[row_idx-1][3:])
28 | land[row_idx][3] += max(land[row_idx - 1][:3])
29 |
30 | return max(land[-1])
31 |
32 | case1 = [[1,2,3,5],[5,6,7,8],[4,3,2,1]]
33 | print(solution(case1)) #16
34 |
35 | case2 = [[1, 1, 3, 1], [2, 3, 2, 2], [1, 4, 1, 1]]
36 | print(solution(case2)) #9
--------------------------------------------------------------------------------
/Programmers/롤케이크 자르기/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-10-06 13:43
3 |
4 | #### 제한 사항
5 | - topping의 길이가 1,000,000까지 이므로 O(N) ~ O(NlogN)의 알고리즘으로 해결해야 함
6 |
7 | #### 풀이
8 | - set으로 변환한 개수를 바탕으로 풀이하면 어떻게 해서든 시간초과
9 | - 시간 복잡도를 해결하기 위해 Counter(dict)와 다른 defaultdict를 활용함
10 | - 우선 Topping의 counter를 만들기 (O(N))
11 | - Topping의 마지막 원소부터 하나씩 defaultdict에 넣기
12 | - Topping.pop()
13 | - pop했을 때 해당 토핑을 카운터에서 -1해주고, 0이 될 경우 del (O(1))
14 | - 매 반복마다 counter의 길이와 defaultdict의 길이를 비교해서 같으면 토핑의 종류의 개수가 같은 것
15 |
16 | 풀이 완료 : 2023-10-06 13:57(풀이 시간 : 14분 소요)
17 | """
18 | from typing import List
19 | from collections import Counter, defaultdict
20 |
21 |
22 | def solution(topping: List[int]) -> int:
23 | answer = 0
24 |
25 | counter = Counter(topping) # 철수 토핑 (카운터(dict))
26 | other = defaultdict(int) # 동생 토핑
27 |
28 | while len(topping) > 1: # 모든 자르는 경우 고려
29 | counter[topping[-1]] -= 1 # 철수 토핑 마지막 부분을 동생한테 옮길 것임
30 | if counter[topping[-1]] == 0: # 옮겼을 떄 해당 종류 토핑이 0개가 되면
31 | del counter[topping[-1]] # 카운터에서 제거
32 | other[topping.pop()] += 1 # 동생한테 철수 마지막 부분 옮기기
33 |
34 | if len(counter) == len(other): # 철수 토핑 종류와 동생 토핑 종류를 비교
35 | answer += 1
36 |
37 | return answer
38 |
39 |
40 | def main() -> None:
41 | case1 = [1, 2, 1, 3, 1, 4, 1, 2]
42 | case2 = [1, 2, 3, 1, 4]
43 |
44 | print(solution(case1)) # 2
45 | print(solution(case2)) # 0
46 |
47 |
48 | main()
49 |
--------------------------------------------------------------------------------
/Programmers/롤케이크 자르기/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 |
5 | topping: 롤케이크에 올려진 토핑들의 번호를 저장한 정수배열(1 ≤ topping의 길이 ≤ 1,000,000)
6 | - 1 ≤ topping의 원소 ≤ 10,000
7 |
8 |
9 | 전체 토핑의 개수와 토핑의 가짓수에서 철수가 먹게되는 토핑의 개수와 가짓수를 계속해서 갱신하며 답을 찾아나간다.
10 | 이 때, set 자료형을 써서 철수가 먹게되는 토핑의 가짓수를 O(1)에 탐색한다.
11 |
12 | <시간복잡도>
13 | O(N)
14 | """
15 | from typing import List
16 | from collections import Counter
17 |
18 |
19 | def solution(topping: List) -> int:
20 | """
21 | topping: 롤케이크에 올려진 토핑들의 번호를 저장한 정수 배열
22 | """
23 | answer = 0 # 롤케이크를 공평하게 자르는 방법의 수
24 | tot = Counter(topping) # 각 토핑의 개수
25 | tot_cnt = len(tot) # 전체 토핑의 가짓수
26 |
27 | # 철수가 먹게되는 토핑의 종류
28 | eat = set()
29 | eat_cnt = 0
30 | for x in topping:
31 | # 기존에 없는 토핑인 경우 -> 토핑을 set에 추가하고, 먹게되는 토핑의 가짓수도 증가
32 | if x not in eat:
33 | eat.add(x)
34 | eat_cnt += 1
35 | # 전체 토핑에서 철수가 먹게되는 토핑의 개수 1 감소
36 | tot[x] -= 1
37 | if tot[x] == 0: # 만약 해당 토핑을 모두 철수가 먹게된다면 동생이 먹는 토핑의 가짓수 1 감소
38 | tot_cnt -= 1
39 | if eat_cnt == tot_cnt: # 철수가 먹는 토핑의 가짓수와 동생이 먹는 토핑의 가짓수가 같아지면 경우의 수 1 증가
40 | answer += 1
41 | elif eat_cnt > tot_cnt: # 철수가 먹는 가짓수가 더 커진다면 더 이상 확인할 필요 없으니 반복문 종료
42 | break
43 |
44 | return answer
45 |
46 |
47 | def main() -> None:
48 | case1 = [1, 2, 1, 3, 1, 4, 1, 2]
49 | case2 = [1, 2, 3, 1, 4]
50 | print(solution(case1)) # 2
51 | print(solution(case2)) # 0
52 |
53 | main()
--------------------------------------------------------------------------------
/Programmers/모음사전/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분 소요
5 |
6 | 접근법
7 | - word 의 길이가 5 이하이고 모음으로만 이루어져있기 때문에 경우의 수가 많이 안나올것같음
8 | - 우선 모든 조합을 구한다음 전체 경우의수를 구해보자
9 | - 중복을 허용하고 순서가 존재하는 경우의수 -> 중복순열
10 | - 전체 경우의 수는 3905 -> 그냥 다 구하고 index 로 접근해도 문제 없음
11 | - 모음 사전의 구성 원리를 잘 이해 못하겠음 -> 그냥 정렬해봤는데 이런 느낌이 맞는듯?!
12 |
13 | 회고
14 | - 규칙을 찾아서 풀어내는 문제 같은데 모음 사전 구성 원리 자체를 이해 못해서 경우의 수를 직접 따져볼 수 없었음
15 |
16 | """
17 |
18 | from itertools import product
19 |
20 | def solution(word):
21 | vowel_dict = []
22 | for i in range(1, 6): # 단어 길이별 중복순열 계산
23 | for w in product("AEIOU", repeat=i):
24 | vowel_dict.append("".join(w)) # 튜플형태로 저장하면 길이가 너무 길기 때문에 문자열로 변환하여 저장
25 |
26 | vowel_dict.sort() # 모음사전 순서대로 정렬
27 | return vowel_dict.index(word) + 1 # 인덱스가 아닌 "번째" 이므로 1 더해줌
--------------------------------------------------------------------------------
/Programmers/모음사전/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 모음사전
3 | https://school.programmers.co.kr/learn/courses/30/lessons/84512
4 |
5 | 풀이시간
6 | 20:50 ~ 20:30 (40분)
7 |
8 | 문제 조건
9 | 모음 5개를 5개 이하의 중복을 허용하여 단어 만들기 -> 3905개
10 | N = 3905
11 |
12 | 시간 복잡도 :
13 | O(N + NlogN + N) = O(모든 요소 생성 + 정렬 + 인덱스 탐색)
14 | O(NlogN)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 중복순열을 사용한 완전탐색
18 |
19 | - 중복순열로 가능한 모든 경우의 수를 생성
20 | - 생성된 사전 정렬
21 | - 인덱스 반환
22 |
23 | 순열 : permutations
24 | 중복순열 : product
25 | 조합 : combinations
26 | 중복조합 : combination_with_replacement
27 |
28 | """
29 | from itertools import product
30 |
31 | def solution(word):
32 | words = "AEIOU"
33 | bag = []
34 |
35 | # 중복 순열로 1,5개의 중복 순열 모두 구하기
36 | for r in range(1, 6):
37 | for item in product(words, repeat=r):
38 | bag.append("".join(item))
39 |
40 | # 데이터 사전 순 정렬
41 | bag_list = sorted(bag)
42 |
43 | # 인덱스 값 반환
44 | return bag_list.index(word) + 1
--------------------------------------------------------------------------------
/Programmers/방문 길이/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분
5 |
6 | 접근법
7 | - visit 배열을 활용하여 풀이 가능
8 | - dirs < 500 으로 시간복잡도도 충분
9 | - visit 딕셔너리의 키는 좌표와 좌표간의 연결
10 |
11 | 회고
12 | - 모든 좌표간의 연결을 저장할 필요 없이 인접 좌표와의 연결만 표현하면 되므로 메모리 낭비
13 | - 다 풀고나서 visit 배열을 다시 정의해서 풀이
14 |
15 | """
16 |
17 | def solution(dirs):
18 | answer = 0
19 | direction = {"U":(0,1), "R":(1,0), "D":(0,-1), "L":(-1,0)}
20 |
21 | all_location = [(x, y) for y in range(11) for x in range(11)] # 좌표 배열 생성
22 | # visit = {f"{loc_1}_{loc_2}":False for loc_1 in all_location for loc_2 in all_location} # 모든 좌표간의 간선 생성 (원래 풀이, 메모리 낭비)
23 |
24 | visit = {}
25 | for loc in all_location: # 인접한 좌표간의 간선 생성 (수정 풀이)
26 | for _, (dx, dy) in direction.items():
27 | new_loc = (loc[0] + dx, loc[1] + dy)
28 | visit[f"{loc}_{new_loc}"] = False
29 |
30 | nx, ny = 5, 5
31 | visit["(5, 5)_(5, 5)"] = True
32 |
33 | for dir in dirs:
34 | dx, dy = direction[dir]
35 | x, y = nx, ny # 이전 좌표
36 | nx, ny = x + dx, y + dy # 현재 좌표
37 |
38 | if ((nx < 0) or (nx >= 11)) or ((ny < 0) or (ny >= 11)): # 좌표경계를 넘으면 무시
39 | nx, ny = x, y # 이전 좌표로 다시 변환
40 | continue
41 |
42 | if not visit[f"({x}, {y})_({nx}, {ny})"]: # 방문한적이 없다면 count + 1
43 | visit[f"({x}, {y})_({nx}, {ny})"] = True # 간선을 True 로 변환
44 | visit[f"({nx}, {ny})_({x}, {y})"] = True # 방향에 상관없이 방문한 길은 마찬가지
45 | answer += 1
46 |
47 | return answer
--------------------------------------------------------------------------------
/Programmers/방문 길이/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-20 10:24
3 |
4 | #### 제한사항 : dirs(명령어 수)는 500, 상관 없을 것 같다.
5 |
6 | #### 풀이
7 | - 수동 BFS..?
8 | - 이동하면서 matrix에다가 방문 체크를 하고, matrix 범위 내 방문 체크가 안되어있는 곳을 밟을 때 카운트
9 | - -5~5범위를 -> 0 ~ 10로 대체
10 | - 주의할 점
11 | - 특정 점을 새로 방문했는지의 여부가 아닌, 그 길을 처음 갔는지에 대해서이다.
12 | - 좌표 별 어디서 그 좌표에 도달했는지 set으로 관리한다.
13 | - (0, 0) -> (1, 1)로 갔다면 (1, 1)의 set에 (0, 0)을 add
14 | - 이때 (1, 1) -> (0, 0)도 같은 길이므로 (0, 0)의 set에 (1, 1)도 add
15 |
16 | 풀이 완료 : 2023-09-20 10:49 (풀이 시간 : 25분)
17 | """
18 |
19 |
20 | def solution(dirs: str) -> int:
21 | matrix = [[set() for _ in range(11)] for _ in range(11)]
22 | y, x = 5, 5 # 초기 위치
23 | directions = {
24 | direction: dydx
25 | for direction, dydx in zip("UDLR", [(-1, 0), (1, 0), (0, -1), (0, 1)])
26 | } # 이동 방향 별 움직여야 할 좌표
27 | cnt = 0
28 |
29 | for direction in dirs:
30 | dy, dx = directions[direction]
31 | if 0 <= y + dy < 11 and 0 <= x + dx < 11: # 다음 좌표가 범위 내이면
32 | by, bx = y, x # 이동 전 좌표
33 | y += dy # 이동 후 좌표
34 | x += dx
35 | if not (by, bx) in matrix[y][x]: # 처음 걸어보는 길이면
36 | cnt += 1
37 | matrix[y][x].add((by, bx)) # 이전 좌표 -> 현재 좌표 방문 처리
38 | matrix[by][bx].add((y, x)) # 현재 좌표 -> 이전 좌표도 같은 길이므로 방문 처리
39 |
40 | return cnt
41 |
42 |
43 | def main() -> None:
44 | case1 = "ULURRDLLU"
45 | case2 = "LULLLLLLU"
46 | case3 = "UDUDUDUDU"
47 |
48 | print(solution(case1)) # 7
49 | print(solution(case2)) # 7
50 | print(solution(case3)) # 7
51 |
52 |
53 | main()
54 |
--------------------------------------------------------------------------------
/Programmers/방문 길이/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 |
5 | - dirs는 string형으로 주어지며, 'U', 'D', 'R', 'L' 이외에 문자는 주어지지 않는다.
6 | - dirs의 길이는 500 이하의 자연수
7 |
8 |
9 | (0,0) -> (1,0)으로 위로 올라가는 거나 (1,0) -> (0,0)으로 내려가는 거나 같은 경로이기 때문에 하나로 통일해서
10 | 지나간 경로에 저장한다. 이 때, 경로를 set 자료형으로 중복이 포함되지 않게 한다.
11 |
12 | <시간 복잡도>
13 | O(n)
14 | """
15 |
16 |
17 | def solution(dirs: str) -> int:
18 | route = set() # 캐릭터가 지나간 경로
19 | # 시작 위치는 (0, 0)
20 | x, y = 0, 0
21 | for dir in dirs:
22 | if dir == 'U' and y < 5: # 위쪽으로 이동
23 | route.add(((x, y), (x, y+1)))
24 | y += 1
25 | elif dir == 'D' and y > -5: # 아래쪽으로 이동
26 | route.add(((x, y-1), (x, y)))
27 | y -= 1
28 | elif dir == 'R' and x < 5: # 오른쪽으로 이동
29 | route.add(((x, y), (x+1, y)))
30 | x += 1
31 | elif dir == 'L' and x > -5: # 왼쪽으로 이동
32 | route.add(((x-1, y), (x, y)))
33 | x -= 1
34 | return len(route)
35 |
36 | """
37 | 테스트 1 〉 통과 (0.02ms, 10.2MB)
38 | 테스트 2 〉 통과 (0.01ms, 10.4MB)
39 | 테스트 3 〉 통과 (0.01ms, 10.2MB)
40 | 테스트 4 〉 통과 (0.04ms, 10.1MB)
41 | 테스트 5 〉 통과 (0.07ms, 10.4MB)
42 | 테스트 6 〉 통과 (0.02ms, 10.4MB)
43 | 테스트 7 〉 통과 (0.01ms, 10.2MB)
44 | 테스트 8 〉 통과 (0.02ms, 10.1MB)
45 | 테스트 9 〉 통과 (0.02ms, 10.2MB)
46 | 테스트 10 〉 통과 (0.02ms, 10.2MB)
47 | 테스트 11 〉 통과 (0.02ms, 10.2MB)
48 | 테스트 12 〉 통과 (0.04ms, 10.2MB)
49 | 테스트 13 〉 통과 (0.07ms, 10.2MB)
50 | 테스트 14 〉 통과 (0.04ms, 10.2MB)
51 | 테스트 15 〉 통과 (0.03ms, 10.2MB)
52 | 테스트 16 〉 통과 (0.15ms, 10.2MB)
53 | 테스트 17 〉 통과 (0.16ms, 10.4MB)
54 | 테스트 18 〉 통과 (0.16ms, 10.1MB)
55 | 테스트 19 〉 통과 (0.16ms, 10.1MB)
56 | 테스트 20 〉 통과 (0.17ms, 10.2MB)
57 | """
--------------------------------------------------------------------------------
/Programmers/스킬트리/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 15분
5 |
6 | 접근법
7 | - 시간복잡도 충분
8 | - 선행 스킬이 필요함 -> 큐로 구현 가능!
9 |
10 | 회고
11 | - 시간복잡도를 고려할 필요가 없었기 떄문에 비교적 수월하게 풀이
12 | - 스택으로도 풀 수 있을것 같긴한데 큐가 선행스킬을 먼저 찍는다는 점에서 직관적으로 다가왔음
13 | - 예외 케이스(25 라인)의 경우 조금 더 최적화 해낼 수 있을듯함
14 | - 여러분들의 코드를 열심히 공부하겠습니다!!!
15 |
16 | """
17 |
18 | from collections import deque
19 |
20 | def solution(skill, skill_trees):
21 | answer = 0
22 |
23 | for skill_tree in skill_trees:
24 | queue = deque(skill)
25 | uncommon_skills = set(skill) - set(skill_tree) # 선행 스킬루트에는 있지만 스킬트리에 해당 스킬을 사용하지 않는 경우
26 |
27 | for each_skill in skill_tree:
28 | if each_skill == queue[0]: # 큐의 첫번째 스킬(선행되어야 하는 스킬) 과 스킬트리의 순서가 일치하는 경우
29 | queue.popleft() # 선행스킬을 마스터한 것으로 판단
30 | if not queue: # 큐가 비었을 떄 중지
31 | break
32 |
33 | if uncommon_skills == set(queue): # 선행스킬을 모두 제대로 사용했을 경우 or 큐에 남아있는 스킬이 사용하지 않는 스킬인 경우
34 | answer += 1
35 |
36 | return answer
--------------------------------------------------------------------------------
/Programmers/스킬트리/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-10-01 16:07
3 |
4 | #### 제한 조건
5 | - 문자열의 길이 <= 26
6 | - 리스트의 길이 <= 20
7 | - 26 * 20 = 5200, 한 번의 순회에서 모든 문자열의 문자를 순회하는 것은 괜찮음
8 |
9 | #### 풀이
10 | - 스킬트리에서 가능한 개수를 세면 됨
11 | - 선행 스킬이 있는 스킬의 경우, 선행 스킬을 배웠는지의 여부를 판단하면 됨
12 | - A -> B -> C 일 때 C에 대해서 B여부만 판단하면 됨 (어차피 A를 안배우면 B를 못배우기 때문)
13 |
14 | 풀이 완료 : 2023-10-01 16:22 (풀이 시간 : 15분 소요)
15 | """
16 | from typing import List
17 |
18 |
19 | def solution(skill: str, skill_trees: List[str]) -> int:
20 | before = {} # 어떤 스킬의 직전 선행 스킬을 담을 dict
21 | answer = 0
22 |
23 | for idx in range(len(skill) - 1):
24 | before[skill[idx + 1]] = skill[idx] # 선행 스킬이 필요한 경우 직전 선행 스킬 담기
25 |
26 | for skill_tree in skill_trees:
27 | learned = {s: False for s in skill} # 선행 스킬을 배웠는지의 여부
28 | is_failed = False # 정답 여부를 판단하기 위한 flag
29 | for sk in skill_tree:
30 | if sk in before and not learned[before[sk]]: # 선행 스킬이 필요한데 안 배운 경우
31 | is_failed = True # Fail
32 | break
33 | learned[sk] = True
34 |
35 | if not is_failed: # 모두 제대로 배웠으면
36 | answer += 1 # 정답
37 |
38 | return answer
39 |
40 |
41 | def main() -> None:
42 | case1 = ["CBD", ["BACDE", "CBADF", "AECB", "BDA"]]
43 |
44 | print(solution(*case1)) # 2
45 |
46 |
47 | main()
48 |
--------------------------------------------------------------------------------
/Programmers/스킬트리/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 30분
3 |
4 |
5 | skill: 선행 스킬 순서(알파벳 대문자)
6 | - 1이상 26이하로 중복해서 주어지지 않음
7 |
8 | 스킬트리: 유저들이 만든 스킬트리
9 | - 1 이상 20이하인 배열
10 | - 각 원소는 길이가 2 이상 26인 문자열로, 스킬은 중복해서 주어지지 않음
11 |
12 |
13 | skill_tree를 하나씩 순회하며 해당 스킬트리가 가능한지 확인한다.
14 | 1) skill_tree의 스킬을 하나씩 확인하면서 선행스킬에 포함되는 스킬만 뽑아 문자열로 만든다.
15 | 2) 위에서 만든 스킬 문자열이 선행 스킬의 순서에 해당되는지 확인한다.
16 | - 여기서 선행스킬에 해당되는게 하나도 없을 때도 카운트 돼야한다. (이 부분을 놓쳐서 오래걸림..)
17 | - ex) skill = "CBD", skill_trees = ["AF"] -> 1
18 |
19 | <시간복잡도>
20 | O(len(skill_trees) * len(skill_tree))
21 | 최대 연산횟수는 20 * 26 = 520
22 | """
23 | from typing import List
24 |
25 |
26 | def solution(skill: str, skill_trees: List[str]) -> int:
27 | answer = 0 # 가능한 스킬트리 개수
28 |
29 | for skill_tree in skill_trees: # 최대 20개
30 | skill_name = ''.join(map(str, [s for s in skill_tree if s in skill]))
31 |
32 | if not skill_name or (skill_name[0] == skill[0] and skill_name in skill):
33 | answer += 1
34 |
35 | return answer
36 |
37 |
38 | """
39 | 테스트 1 〉 통과 (0.01ms, 10.5MB)
40 | 테스트 2 〉 통과 (0.01ms, 10.5MB)
41 | 테스트 3 〉 통과 (0.01ms, 10.7MB)
42 | 테스트 4 〉 통과 (0.01ms, 10.5MB)
43 | 테스트 5 〉 통과 (0.02ms, 10.6MB)
44 | 테스트 6 〉 통과 (0.01ms, 10.7MB)
45 | 테스트 7 〉 통과 (0.02ms, 10.5MB)
46 | 테스트 8 〉 통과 (0.01ms, 10.6MB)
47 | 테스트 9 〉 통과 (0.03ms, 10.5MB)
48 | 테스트 10 〉 통과 (0.03ms, 10.5MB)
49 | 테스트 11 〉 통과 (0.02ms, 10.5MB)
50 | 테스트 12 〉 통과 (0.02ms, 10.6MB)
51 | 테스트 13 〉 통과 (0.02ms, 10.5MB)
52 | 테스트 14 〉 통과 (0.01ms, 10.7MB)
53 | """
--------------------------------------------------------------------------------
/Programmers/스킬트리/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 스킬트리
3 | https://school.programmers.co.kr/learn/courses/30/lessons/49993
4 |
5 | 풀이시간
6 | 10:23 ~ 10:39 (16분)
7 |
8 | 문제 조건
9 | 1 <= len(skill) <= 26
10 | 1 <= len(skill_trees) <= 20
11 | 2 <= 각 skill_trees의 길이 <= 26
12 |
13 | 시간 복잡도 :
14 | O(20 * 26)
15 |
16 | 접근법
17 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 완전 탐색
18 |
19 | 데이터의 중복과 갯수가 크게 문제 되지 않기 때문에 완전 탐색으로 단순 구현을 진행하였습니다.
20 |
21 | 완전 탐색 과정
22 | 각각의 스킬트리 과정을 확인합니다.
23 | - 선행 스킬의 인덱스를 증가시키며 선행 스킬이 순서대로 주어지는지 확인하기
24 | - 스킬트리의 스킬을 하나씩 확인하고 선행 스킬에 포함된다면 등장해야하는 선행 스킬과 비교
25 | - 정상일 경우 다음 선행 스킬의 인덱스로 증가
26 | - 아니라면 인터럽트
27 | - 인터럽트가 일어나지 않은 스킬트리는 정답으로 인정
28 | """
29 |
30 | def solution(skill, skill_trees):
31 | answer = 0
32 |
33 | # 선행 스킬의 Set화
34 | set_skill = set(skill)
35 |
36 | # 선행 스킬의 List화
37 | skill = list(skill)
38 |
39 | # 주어진 모든 스킬트리 검사
40 | for skill_tree in skill_trees:
41 | # 현재 검사해야하는 선행 스킬의 인덱스
42 | skill_idx = 0
43 |
44 | # 정상적으로 검사가 종료되었는지 Interrupt 여부 확인
45 | interrupt = False
46 |
47 | # 현재 스킬트리의 스킬을 하나씩 검사
48 | for alpha in skill_tree:
49 | # 현재 스킬트리의 스킬이 선행 스킬 내부에 존재하고
50 | if alpha in set_skill:
51 | # 현재 검사해야하는 선행 스킬의 인덱스를 가지는 스킬과 같다면 정상
52 | if alpha == skill[skill_idx]:
53 | # 다음 선행 스킬을 검사합니다.
54 | skill_idx += 1
55 |
56 | # 만약 다르다면 비정상 인터럽트
57 | else:
58 | interrupt = True
59 | break
60 |
61 | # 인터럽트가 진행되지 않았다면 정상적으로 검사 완료
62 | if not interrupt:
63 | answer += 1
64 |
65 | return answer
--------------------------------------------------------------------------------
/Programmers/아방가르드 타일링/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 17:30 -> ... 해결 x
3 | [풀이 참고](https://magentino.tistory.com/172)
4 |
5 | 특별 경우의 수 : i=4, 7, ..., i=5, 8, ...일 때 2, i=6, 9, ...일 때 4 가 반복됨을 활용해 점화식을 구하고,
6 | 각 SUM을 memoization하는 과정에서 업데이트 하는 방법으로 최적화
7 | """
8 |
9 | def solution(n):
10 | init = [0, 1, 3, 10]
11 |
12 | if n < 4:
13 | return init[n]
14 |
15 | dp = init + [0] * (n-3) # n에 해당하는 경우의 수를 담을 memoization table
16 | dp_sum = sum(init) # 1번째 Term : 전체 경우의 수 SUM
17 | partial_dp_sum = [10, 1, 3] # 2번째 Term : i가 3, 6, 9, ... / 1, 4, 7, ... / 2, 5, 8, ... 각각의 SUM
18 |
19 |
20 |
21 | for i in range(4, n+1):
22 | _add = 2 if i%3 else 4 # 특별 경우의 수 : i=4, 7, ..., i=5, 8, ...일 때 2, i=6, 9, ...일 때 4
23 | dp[i] = (2 * dp_sum + 2*partial_dp_sum[i%3] - dp[i-1] + dp[i-3] + _add) # 점화식
24 | dp_sum = (dp_sum + dp[i]) # 전체 SUM update
25 | partial_dp_sum[i%3] = (partial_dp_sum[i%3] + dp[i]) # 부분 SUM update
26 |
27 | return dp[n] % 1000000007
28 |
29 |
30 |
31 | case1 = 2
32 | case2 = 3
33 | case3 = 4
34 | print(solution(case1)) # 3
35 | print(solution(case2)) # 10
36 | print(solution(case3)) # 23
--------------------------------------------------------------------------------
/Programmers/아방가르드 타일링/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시간: 1시간 30분
3 | - D[x+3] - D[x]를 빼서 D[x] 점화식을 구해주는 아이디어 참고
4 |
5 |
6 | n : 가로 길이 (1 ≤ n ≤ 100,000)
7 |
8 |
9 | 1. 테이블 정의하기
10 | D[i] = i x 3 크기의 판을 타일링하는 방법의 수
11 |
12 | 2. 점화식 찾기
13 | D[i] = D[i-1] + 2*D[i-2] + 6*D[i-3] + D[i-4] - D[i-6]
14 |
15 | 3. 초기값 정하기
16 | D[1] = 1, D[2] = 3, D[3] = 10
17 |
18 | <후기>
19 | 전형적인 DP문제로 백준의 2 x n 타일링 시리즈와 매우 비슷하지만 점화식을 도출하기 좀 더 까다롭다. (가장 비슷한 건, 프로그래머스의 3 x n 타일링 문제)
20 | 이 문제에서 중요한 점은 두 가지인데
21 | 1. n = 4, 5, 6일 때 완전히 새롭게 나타나는 타일링 방법의 수가 각각 2, 2, 4개 이며 이후 계속 2, 2, 4개가 반복해서 발생한다는 점을 캐치해야 한다.
22 | 2. 점화식을 도출할 때, D[x] = D[x-1] * s[1] + D[x-2] * s[2] + D[x-3] * s[3] + D[x-4] * s[4] + D[x-5] * s[5] + D[x-6] * s[6] + ... + s[x]에서
23 | s[4]부터 2, 2, 4가 반복되므로, D[x+3]를 사용해 중복되는 부분을 제거해야 한다.
24 | 결과적으로 D[x+3] - D[x]를 해줌으로써 D[x]의 최종 점화식을 도출할 수 있게 된다.
25 |
26 | 처음에 1번까지는 생각했는데 S배열을 따로 만들어줘야 하나 고민을 하다가, 그렇게 되면 이중 for문이 돼 시간복잡도가 터지게 되고 어차피 S[4]부터 2, 2, 4가 반복되기 때문에 굳이 별도로 배열을 만들어 줄 필요가 없다는 점을 알았다.
27 | 이후에 D[x+3]에서 D[x]를 빼줄 생각은 못 했는데 새로운 부분을 배울 수 있었다.
28 | """
29 | def solution(n):
30 | answer = 0
31 | d = [0] * 100001 # d[i]: i x 3 크기의 판을 타일링하는 방법의 수
32 | mod = 1000000007
33 |
34 | # 초기값 설정
35 | d[0] = 1
36 | d[1] = 1
37 | d[2] = 3
38 | d[3] = 10
39 |
40 | for i in range(4, n+1):
41 | # 점화식
42 | d[i] = d[i-1] + 2 * d[i-2] + 6 * d[i-3] + d[i-4] - d[i-6]
43 | d[i] %= mod
44 |
45 | answer = d[n]
46 | return answer
47 |
48 |
49 | print(solution(9))
50 |
51 |
--------------------------------------------------------------------------------
/Programmers/아방가르드 타일링/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 아방가르드 타일링
3 | https://school.programmers.co.kr/learn/courses/30/lessons/181186
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> DP
7 |
8 | - dp로 갯수의 패턴을 찾고 점화식을 세워 풀이
9 |
10 | - 풀이 참고 (https://schini.tistory.com/entry/아방가르드-타일링-181186번-프로그래머스-Programmers)
11 | """
12 |
13 |
14 | def solution(n):
15 | dp = [1, 1, 3, 10, 23, 62]
16 |
17 | if n <= 5:
18 | return dp[n]
19 |
20 | four_iter = [2, 2, 0]
21 | five_iter = [2, 0, 0]
22 | six_iter = [0, 0, 0]
23 |
24 | for i in range(6, n + 1):
25 | current = dp[-1] + 2 * dp[-2] + 5 * dp[-3]
26 |
27 | idx = (i - 2) % 3
28 | five_iter[idx] += dp[1] * 2
29 | # print(f"dp[1] : {dp[1]}\n")
30 | # print(f"dp[1] * 2] : {dp[1] * 2}")
31 | # print(f"four_iter[{idx}] : {five_iter[idx]}")
32 | current += five_iter[idx]
33 |
34 | idx = (i - 1) % 3
35 | four_iter[idx] += dp[2] * 2
36 | # print(f"dp[2] : {dp[2]}\n")
37 | # print(f"dp[2] * 2 : { dp[2] * 2}")
38 | # print(f"four_iter[{idx}] : { four_iter[idx]}")
39 | current += four_iter[idx]
40 |
41 | idx = i % 3
42 | six_iter[idx] += dp[0] * 4
43 | # print(f"dp[0] : {dp[0]}\n")
44 | # print(f"dp[0] *4 : {dp[0] *4}")
45 | # print(f"six_iter[{idx}] : {six_iter[idx]}")
46 | current += six_iter[idx]
47 |
48 | dp = dp[1:] + [current]
49 |
50 | return dp[-1] % 1_000_000_007
51 |
52 |
53 | print(solution(10))
54 |
--------------------------------------------------------------------------------
/Programmers/압축/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 46분
5 |
6 | 접근법
7 | - n 이 1000 이하 -> O(N^2) 이상의 풀이도 가능
8 | - 특정 알고리즘을 예시까지 친절히 주었고 시간복잡도 충분 -> 일단 구현해보자
9 | - 초기 단어사전을 딕셔너리로 구현하려했지만 리스트로해도 문제없음 -> 리스트로 구현
10 | - 업데이트되는 단어가 가변성을 띄는 것을 주의
11 | - 시간복잡도가 충분하기 때문에 반복문으로 구현
12 |
13 | 회고
14 | - 예제를 친절하게 제공해줘서 풀이하기가 비교적 수월했음
15 | - 이후에 궁금해서 검색해보니 해시 테이블로 구현되어있는 딕셔너리를 사용하는게 시간복잡도를 더 줄일 수 있는 방법이었던것 같음
16 |
17 | """
18 |
19 | def solution(msg):
20 | answer = []
21 | words = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") # 단어 사전 초기화 단계
22 |
23 | idx = -1
24 | while idx < len(msg) - 1: # 전체 단어들을 순회
25 | idx += 1
26 | temp_word = msg[idx] # 첫 글자 입력
27 |
28 | for len_word in range(2, len(words)): # 다음 글자들이 사전에 존재하는지 확인
29 | new_word = msg[idx:idx + len_word]
30 |
31 | if new_word in words: # 만약 단어사전에 존재하면 첫 글자가 아닌 해당 단어로 대체
32 | temp_word = new_word
33 | else:
34 | words.append(new_word) # 만약 단어사전에 존재하지 않으면 단어사전에 추가하고 반복문 종료
35 | break
36 |
37 | answer.append(words.index(temp_word) + 1) # 최종적으로 결정된 단어 색인 번호 추가
38 | idx += len(temp_word) - 1 # 순회 시작 인덱스를 변경 (입력 단어가 첫 글자가 아닌 경우에만 업데이트됨)
39 |
40 | return answer
41 |
42 |
43 | print(solution("KAKAO")) # [11, 1, 27, 15]
44 | print('----------')
45 | print(solution("TOBEORNOTTOBEORTOBEORNOT")) # [20, 15, 2, 5, 15, 18, 14, 15, 20, 27, 29, 31, 36, 30, 32, 34]
46 | print('----------')
47 | print(solution("ABABABABABABABAB")) # [1, 2, 27, 29, 28, 31, 30]
--------------------------------------------------------------------------------
/Programmers/압축/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023-08-22 14:43
3 |
4 | - 주어지는 문자열의 길이는 1000글자 이하이므로 문자열을 활용하는 로직은 O(N^2)까지 적용 가능
5 | - 색인 번호를 담을 해시 테이블을 만들어서 관리
6 | - 투 포인터(i, j)를 활용해서 색인을 참조할 문자열을 결정
7 | - 기준 문자(i) 부터 (사전에 있는 단어 + 다음 단어)가 없을 때까지 j를 증가시켜 사전에 있는 단어는 출력, 없는 단어는 등록을 반복하면 된다.
8 |
9 | 풀이 완료 : 2023-08-22 15:19 (풀이 시간 : 36분)
10 | '''
11 |
12 | from typing import List, Dict
13 |
14 | def solution(msg: str) -> List[int]:
15 |
16 | hash_table: Dict[str:int] = {ch:idx+1 for idx, ch in enumerate('ABCDEFGHIJKLMNOPQRSTUVWXYZ')} # 길이가 1인 모든 사전 단어 초기화
17 | answer: List[int] = []
18 | i: int = 0
19 | count: int = 26 # 현재 사전에 26개 초기화 되어 있음
20 |
21 | while i < len(msg): # 기준 문자(i)가 문자열 순회를 마칠 때까지
22 | j=i+1 # j는 i+1로 초기화 / 'apple'을 예시 'apple'[0:1] -> 'a'
23 |
24 | while j < len(msg) and msg[i:j+1] in hash_table: # (사전에 있는 단어 + 다음 단어)가 사전에 없을 때까지
25 | j+=1 # 'apple'의 경우 'a'는 사전에 있고, 'ap' 는 사전에 없으므로 j는 2까지 증가
26 |
27 | answer.append(hash_table[msg[i:j]]) # 사전에 있는 단어는 출력 / 'a'는 출력
28 | count += 1
29 | hash_table[msg[i:j+1]] = count # 사전에 없는 단어는 count 증가 시켜서 사전에 넣기 / 'ap'는 사전에 다음 색인 번호로 인덱싱해서 추가
30 | i=j # 출력 다음 부분부터 다시 탐색 시작
31 |
32 | return answer
33 |
34 | def main() -> None:
35 | case1 = 'KAKAO'
36 | case2 = 'TOBEORNOTTOBEORTOBEORNOT'
37 | case3 = 'ABABABABABABABAB'
38 |
39 | print(solution(case1)) # [11, 1, 27, 15]
40 | print(solution(case2)) # [20, 15, 2, 5, 15, 18, 14, 15, 20, 27, 29, 31, 36, 30, 32, 34]
41 | print(solution(case3)) # [1, 2, 27, 29, 28, 31, 30]
42 |
43 | main()
--------------------------------------------------------------------------------
/Programmers/압축/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 압축
3 | https://school.programmers.co.kr/learn/courses/30/lessons/17684
4 |
5 | 풀이시간 22:30 ~ 23:00 | 23:53 ~ 00:27 (1시간 4분)
6 |
7 | 접근법
8 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 구현
9 |
10 | 1. W(현재 입력) 값을 찾기
11 | - 가장 긴 사전에 있는 문자열 w를 찾기
12 | 2. 가장 긴 W의 사전 인덱스 값을 answer에 삽입
13 | 3. W+C를 사전에 등록하기
14 |
15 | """
16 | from typing import List
17 |
18 |
19 | def solution(msg: str):
20 | answer: List = []
21 |
22 | msg_len: int = len(msg)
23 | cur: int = 0
24 | last_index: int = 26
25 |
26 | # 사전 구현
27 | bag = {chr(65 + code): code + 1 for code in range(26)}
28 |
29 | while cur < msg_len:
30 | # 1. w(현재 입력)가정 값을 지정
31 | w = msg[cur]
32 |
33 | # 1. 가장 긴 문자열 w를 찾기
34 | while cur + 1 < msg_len and w + msg[cur + 1] in bag:
35 | w += msg[cur + 1]
36 | cur += 1
37 | # print("2 : ", w)
38 |
39 | # 2. 가장 긴 W의 사전 인덱스 값을 answer에 삽입
40 | answer.append(bag[w])
41 |
42 | # 3. 사전에 w+c를 등록
43 | if cur + 1 < msg_len and w + msg[cur + 1] not in bag:
44 | last_index += 1
45 | bag[w + msg[cur + 1]] = last_index
46 |
47 | cur += 1
48 |
49 | return answer
50 |
51 |
52 | case1 = "KAKAO"
53 | case2 = "TOBEORNOTTOBEORTOBEORNOT"
54 | case3 = "ABABABABABABABAB"
55 |
56 | print(solution(case1))
57 | print(solution(case2))
58 | print(solution(case3))
59 |
--------------------------------------------------------------------------------
/Programmers/야근 지수/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 |
5 | - works: 길이 1 이상, 20,000 이하인 배열
6 | - works의 원소는 50000 이하인 자연수
7 | - n: 1,000,000 이하인 자연수
8 |
9 |
10 | a^2 + b^2 + ... + x^2이 최소가 되려면 계속해서 가장 작업량이 큰 수를 먼저 처리해야 함
11 | 예를 들어, [7, 8] 작업이 있다고 생각했을 때 당연하게도 8을 7로 줄이는 것이 야근 피로도가 최소가 되고
12 | [7, 7]일 때는 어떤 것을 줄이든 상관없음. [7, 6]이 됐을 때는 다시 7을 6으로 줄이는 것이 야근 피로도를 최소화할 수 있게 됨
13 | -> 이를 통해 항상 전체 작업 중 작업량이 가장 큰 값을 먼저 처리해야 됨을 알 수 있음
14 | -> works의 길이가 최대 20,000이기 때문에 n번 max함수를 쓰면 시간초과가 나기 때문에 우선순위큐를 사용해서 최대값을 찾을 수 있음
15 |
16 | <시간복잡도>
17 | O(n): 최대 퇴근까지 남은 시간 n번만 확인하면 됨
18 |
19 | """
20 | import heapq
21 | from typing import List
22 |
23 | def solution(n: int, works: List) -> int:
24 | heap = []
25 | for x in works: # 최대힙으로 구현(작업량이 가장 큰 값)
26 | heapq.heappush(heap, -x)
27 | for i in range(n): # 퇴근까지 남은 시간동안 반복
28 | if not heap: # 더 이상 작업량이 없으면 종료
29 | break
30 | max_num = -heapq.heappop(heap) # 가장 작업량이 큰 업무
31 | max_num -= 1 # 업무 처리
32 | if max_num != 0: # 여전히 작업량이 남은 일만 다시 힙에 추가
33 | heapq.heappush(heap, -max_num)
34 | answer = sum([(-work)**2 for work in heap]) # 야근 피로도 계산
35 | return answer
36 |
37 | """
38 | 정확성 테스트
39 | 테스트 1 〉 통과 (0.05ms, 10.2MB)
40 | 테스트 2 〉 통과 (0.01ms, 10.2MB)
41 | 테스트 3 〉 통과 (0.01ms, 10.4MB)
42 | 테스트 4 〉 통과 (0.07ms, 10.2MB)
43 | 테스트 5 〉 통과 (0.01ms, 10.3MB)
44 | 테스트 6 〉 통과 (0.01ms, 10.3MB)
45 | 테스트 7 〉 통과 (0.01ms, 10.2MB)
46 | 테스트 8 〉 통과 (0.48ms, 10.2MB)
47 | 테스트 9 〉 통과 (0.77ms, 10.3MB)
48 | 테스트 10 〉 통과 (0.01ms, 10.2MB)
49 | 테스트 11 〉 통과 (0.01ms, 10.2MB)
50 | 테스트 12 〉 통과 (0.01ms, 10.3MB)
51 | 테스트 13 〉 통과 (0.01ms, 10.2MB)
52 |
53 | 효율성 테스트
54 | 테스트 1 〉 통과 (418.78ms, 10.1MB)
55 | 테스트 2 〉 통과 (427.85ms, 10.2MB)
56 | """
--------------------------------------------------------------------------------
/Programmers/연속된 부분 수열의 합/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 30분
5 |
6 | 접근법
7 | - 비내림차순이므로 시작 인덱스가 고정되어 있으면 종료 인덱스는 특정 값까지의 합이 k 를 넘는이상 종료됨
8 | - 따라서 시작 인덱스를 반복문으로 시작하기로 함
9 | - 배열에 저장하지 않고 갱신 방식으로 답 저장 방식 채택
10 | - 연산 속도 줄일 수 있음
11 | - 처음에는 슬라이싱 연산을 매번해서 sum 을 했는데, 부분 수열의 합이고 업데이트 되는것은 인덱스들이기 때문에 매번 합을 할 필요가 없었음
12 | - 슬라이싱 연산 시간복잡도 O(end-start) -> 합을 변수에 저장해서 갱신하자!
13 |
14 | 회고
15 | - 생각보다 쉬운것 같다하고 풀었는데 테케에서 절반 정도 시간초과 나서 당황
16 | - 그림을 다시 짜느라 고생했음 ... 처음부터 시간복잡도 고려를 꼼꼼히 해야하는데 아직 습관이 안된 것 같다 ㅠㅠ
17 |
18 | """
19 |
20 | def solution(sequence, k):
21 | answer = []
22 | end_idx = 0
23 | seq_len = len(sequence)
24 |
25 | for start_idx in range(len(sequence)):
26 | if start_idx == 0: # 맨 처음 시작할때의 임시 합 정의
27 | tmp_sum = sequence[start_idx]
28 | else:
29 | tmp_sum -= sequence[start_idx -1] # 부분수열이 업데이트 되었으므로 이전 값 제거
30 |
31 | while end_idx <= len(sequence): # 마지막 시퀀스까지 루프 돌기
32 | if tmp_sum < k: # 시작 인덱스가 고정일 때 합이 k 보다 작으면 종료 인덱스 늘려야 함
33 | end_idx += 1
34 | if end_idx < len(sequence): # 오류 픽스 코드
35 | tmp_sum += sequence[end_idx]
36 |
37 | elif tmp_sum > k: # 합이 k 를 넘으면 종료 인덱스를 늘릴 필요가 없고 시작 인덱스를 늘려야 함
38 | break
39 |
40 | elif tmp_sum == k: # 합이 k 인 경우, 즉 정답
41 | if (end_idx - start_idx) < seq_len: # 부분 수열의 길이를 미리 저장해놓고 이것보다 짧을 때만 정답 갱신
42 | answer = [start_idx, end_idx]
43 | seq_len = end_idx - start_idx
44 | break
45 |
46 | return answer
47 |
48 | print(solution([1,2,3,4,5], 7)) # [2,3]
49 | print(solution([1,1,1,2,3,4,5], 5)) # [6,6]
50 | print(solution([2,2,2,2,2], 6)) # [0,2]
--------------------------------------------------------------------------------
/Programmers/연속된 부분 수열의 합/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | <시간>
3 | 20분
4 |
5 |
6 | - 5 ≤ sequence의 길이 ≤ 1,000,000
7 | - sequence는 비내림차순으로 정렬
8 |
9 |
10 | - 투포인터의 가장 기본적인 문제
11 | - start, end 포인터를 0번 인덱스부터 시작해서 부분합을 확인한다.
12 | - start부터 end까지의 부분합이 k와 같을 때까지 증가 end를 1 증가시킨다. (왜냐하면, sequence의 원소는 항상 자연수이기 때문에 가능)
13 | - k는 항상 sequence의 부분 수열로 만들 수 있는 값이기 때문에 만들 수 없는 경우를 고려할 필요 없다.
14 | - 부분합이 k와 같다면, 길이를 갱신해줘야 할 때만 갱신해주면 된다.
15 | - 이후, 같은 과정을 start를 한 칸 씩 이동하면서 확인해주면 된다.
16 |
17 | 시간복잡도 : O(n)
18 | - start를 0번 인덱스부터 마지막 인덱스까지 모두 확인한다.
19 | - 내부 while문은 for문에 비례하여 n번 실행되는 것이 아니기 때문이다.("end"의 증가 횟수가 "start"의 증가 횟수와 독립적)
20 | """
21 |
22 |
23 | def solution(sequence, k):
24 | n = len(sequence)
25 | diff = n # 길이는 최대로 초기화
26 | end = 0
27 | interval_sum = 0 # 부분합
28 |
29 | # start를 차례대로 증가시키며 반복
30 | for start in range(n):
31 | # end를 가능한만큼 이동시키기
32 | while interval_sum < k and end < n:
33 | interval_sum += sequence[end]
34 | end += 1
35 | # 부분합이 k이면서, 길이가 짧을 때 갱신
36 | if interval_sum == k and end - 1 - start < diff:
37 | answer = [start, end - 1]
38 | diff = end - 1 - start
39 | interval_sum -= sequence[start]
40 |
41 | return answer
42 |
43 |
44 | sequence = [1, 1, 1, 2, 3, 4, 5]
45 | k = 5
46 |
47 |
48 | print(solution(sequence, k))
--------------------------------------------------------------------------------
/Programmers/연속된 부분 수열의 합/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 연속된 부분 수열의 합
3 | https://school.programmers.co.kr/learn/courses/30/lessons/178870?
4 |
5 | 접근법
6 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 투 포인터
7 |
8 | - 정렬된 데이터와 연속된 부분 집합을 보았을 때 투 포인터 문제임을 직감 할 수 있었다.
9 | - 테스트 케이스에 대해서 16,25번이 계속 틀려서 확인해보니 min_interval을 999995로 설정하였는데 값에 대한 범위이기에 1000001이 되어야했었다.
10 | - 투 포인터의 전형적인 케이스는 일정 값을 기준으로 작거나 클때 start,end 포인트를 조정하는 방식이었는데 이번에는 start 지점을 증가시키는 방향을 고정하고 진행했어야 풀이가 진행될 수 있었다.
11 |
12 | - 풀이 참고 (https://velog.io/@wodnr0710/프로그래머스-LV.2-연속된-부분-수열의-합)
13 | """
14 |
15 |
16 | def solution(sequence: list, k: int):
17 | # 정답 반환
18 | answer: list = []
19 |
20 | # 투 포인터의 end 지점
21 | end: int = 0
22 |
23 | # 누적합
24 | cumsum: int = 0
25 |
26 | # 간격이 1000000가 최대 값
27 | min_interval: int = 1000001
28 | s_length: int = len(sequence)
29 |
30 | # 조건에서 "길이가 짧은 수열이 여러 개인 경우 앞쪽(시작 인덱스가 작은)에 나오는 수열을 찾습니다."이기 때문에 앞쪽부터 탐색을 진행한다.
31 | # 시작점을 기준으로 탐색을 진행한다.
32 | for start in range(s_length):
33 | # 시작점을 기준으로 누적합이 K를 넘지 않는지 확인하며 end 증가 시키기
34 | while cumsum < k and end < s_length:
35 | cumsum += sequence[end]
36 | end += 1
37 |
38 | # 누적합 결과가 k와 같다면
39 | if cumsum == k:
40 | # start와 end의 간격을 확인하고
41 | interval: int = end - start - 1
42 |
43 | # 최소 간격보다 작으면 정답과 최소 간격을 업데이트한다.
44 | if interval < min_interval:
45 | min_interval = interval
46 | answer = [start, end - 1]
47 |
48 | # 시작점 변경하기 위해 포함되지 않는 start 값을
49 | cumsum -= sequence[start]
50 |
51 | return answer
52 |
53 |
54 | print(solution([1, 2, 3, 4, 5], 7))
55 | print(solution([1, 1, 1, 2, 3, 4, 5], 5))
56 | print(solution([2, 2, 2, 2], 6))
57 |
--------------------------------------------------------------------------------
/Programmers/연속된 부분 수열의 합/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | - 소요 시간: 50분 + 30분(풀이 참고)
3 |
4 | - 초기 접근 방법 => 시간 초과
5 | - 시작 인덱스를 고정하고, 부분 수열의 길이가 1~n일 때 각 경우의 합을 모두 구함
6 | - 시작 인덱스가 증가하는 만큼, 만들 수 있는 부분 수열의 개수는 짧아짐
7 | - 부분 수열의 합이 k인 경우 해당 부분 수열 저장
8 | - 부분 수열을 키로, 시작 인덱스와 마지막 인덱스를 값으로 하는 딕셔너리로 저장
9 | - 여러 부분 수열을 길이, 인덱스 순으로 정렬해 첫 번째 원소를 답으로 리턴
10 |
11 | - [참고](https://safetymo.tistory.com/14)
12 | - 투 포인터를 사용
13 | - 합이 작으면 오른쪽 포인터를 이동하고, 합이 크거나 같으면 왼쪽 포인터를 이동
14 |
15 | """
16 |
17 | # # 초기 접근 방법 => 시간 초과
18 | # def solution(sequence, k):
19 | # subseq = []
20 | # len_seq = len(sequence)
21 |
22 | # for i in range(len_seq): # 시작 인덱스
23 | # for j in range(len_seq-i): # 마지막 인덱스 + 1
24 | # if sum(sequence[i:i+j+1]) == k:
25 | # subseq.append([i, i+j])
26 |
27 | # subseq = sorted(subseq, key=lambda x: x[1]-x[0])
28 | # return subseq[0]
29 |
30 | def solution(sequence, k):
31 | answer = []
32 | right = 0
33 | sum_subseq = 0
34 |
35 | for left in range(len(sequence)):
36 | while right < len(sequence) and sum_subseq < k: # 합이 k보다 작으면
37 | sum_subseq += sequence[right] # 오른쪽 원소를 계속 더함 (오른쪽 포인터를 오른쪽으로 이동)
38 | right += 1
39 |
40 | if sum_subseq == k:
41 | if not answer:
42 | answer = [left, right-1]
43 | else:
44 | # 기존 부분 수열의 길이가, 현재 구한 부분 수열의 길이보다 크면 현재 구한 부분 수열을 answer로 저장. 아니면 기존 부분 수열을 answer로 유지
45 | answer = [left, right-1] if answer[1] - answer[0] > right - 1 - left else answer
46 |
47 | sum_subseq -= sequence[left] # 왼쪽에 있는 원소를 제외 (왼쪽 포인터를 오른쪽으로 이동)
48 |
49 | return answer
--------------------------------------------------------------------------------
/Programmers/오픈채팅방/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 15분
5 |
6 | 접근법
7 | - N이 1만 -> O(N^2) ~ O(NlogN)
8 | - 유저가 나중에 닉변하는 경우를 생각해서 결과를 순회할 때 배열에 저장하는 행위를 한번 해야함
9 | - 고유 아이디는 그대로 -> 이거를 딕셔너리로 활용하자
10 |
11 | 회고
12 | - 시간복잡도나 코드를 조금 더 간결하게 할 수 있을 것 같다는 생각
13 | - 여러분들의 코드를 잘 공부해보겠습니다!!
14 |
15 | """
16 |
17 | from collections import defaultdict
18 |
19 | def solution(records):
20 | users = defaultdict()
21 | answer = []
22 |
23 | for record in records:
24 | behavior, code, *user = record.split()
25 |
26 | if user: # 닉네임이 비어있지 않은 경우 닉네임 갱신(Leave 가 아닌 경우)
27 | users[code] = user # {유저의 고유 아이디 : 닉네임}
28 |
29 | if behavior == "Enter":
30 | answer.append([code, "님이 들어왔습니다."])
31 | elif behavior == "Leave":
32 | answer.append([code, "님이 나갔습니다."])
33 |
34 | for i in range(len(answer)):
35 | code, printer = answer[i]
36 | answer[i] = users[code][0] + printer # 유저의 고유 아이디를 닉네임으로 변경하며 완전한 문장형태로 변환
37 |
38 | return answer
--------------------------------------------------------------------------------
/Programmers/오픈채팅방/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-09-19 10:40
3 |
4 | #### 제한 사항
5 | - record의 길이는 100,000 이하이므로 O(NlogN) 이하의 알고리즘을 설계해야 한다.
6 |
7 | #### 풀이
8 | - 채팅방 입/퇴장 시 이름 메시지 출력
9 | - 이름바꿔 들어오면 기존 메시지 이름도 바뀜 : 메시지와 사용자 정보를 끝까지 가지고 있어야 함
10 | - 닉넴 중복 허용
11 |
12 | - record를 총 두 번 탐색해, 첫 탐색에서 user id 별 최종 닉네임을 탐색하고, 두 번째 탐색에서 메시지를 출력한다.
13 | - O(2N)으로 해결 가능하다. (startswith(str)는 앞에서부터 str 만큼만 비교하므로 상수 시간으로 계산)
14 |
15 | 풀이 완료 : 2023-09-19 10:55 (풀이 시간 : 15분 소요)
16 | """
17 | from typing import List
18 |
19 |
20 | def solution(record: List[str]) -> List[str]:
21 | """
22 | 채팅방에 들어오고 나가거나, 닉네임을 변경한 기록이 담긴 문자열 배열 record가 매개변수로 주어질 때,
23 | 모든 기록이 처리된 후, 최종적으로 방을 개설한 사람이 보게 되는 메시지를 문자열 배열 형태로 return 한다.
24 | """
25 | id2name = {} # user_id 별 닉네임을 담을 dict
26 | result = [] # 최종 메시지를 담을 list
27 |
28 | for info in record: # user_id 별 이름 get
29 | if not info.startswith("Leave"): # 이름이 바뀔 수 있는 경우는 다시 들어오거나 Change인 경우
30 | _, user_id, name = info.split()
31 | id2name[user_id] = name
32 |
33 | for info in record: # Enter인지, Leave인지에 따라 메시지를 담는다.
34 | if info.startswith("Enter"):
35 | _, user_id, _ = info.split()
36 | result.append(f"{id2name[user_id]}님이 들어왔습니다.")
37 | elif info.startswith("Leave"):
38 | _, user_id = info.split()
39 | result.append(f"{id2name[user_id]}님이 나갔습니다.")
40 |
41 | return result
42 |
43 |
44 | def main() -> None:
45 | case1 = [
46 | "Enter uid1234 Muzi",
47 | "Enter uid4567 Prodo",
48 | "Leave uid1234",
49 | "Enter uid1234 Prodo",
50 | "Change uid4567 Ryan",
51 | ]
52 |
53 | print(
54 | solution(case1)
55 | ) # ["Prodo님이 들어왔습니다.", "Ryan님이 들어왔습니다.", "Prodo님이 나갔습니다.", "Prodo님이 들어왔습니다."]
56 |
57 |
58 | main()
59 |
--------------------------------------------------------------------------------
/Programmers/오픈채팅방/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 오픈채팅방
3 | https://school.programmers.co.kr/learn/courses/30/lessons/42888
4 |
5 | 풀이시간
6 | 20 : 48 ~ 21 : 03 (15분)
7 |
8 | 문제 조건
9 | 1 <= record(R) <= 100,000
10 |
11 | 시간 복잡도 :
12 | O(R + R) = O(R)
13 |
14 | 접근법
15 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 시뮬레이션
16 |
17 | - 문제 내용에 따라 uid 저장 테이블과 들어오고 나올 때를 records에 기록하여 최종적 변경된 닉네임으로 채팅 서비스 기록 남기기
18 |
19 | """
20 | def solution(record):
21 | answer = []
22 |
23 | # uid와 닉네임 테이블 정의
24 | table = dict()
25 |
26 | # 입장/퇴장 기록 추가
27 | records = []
28 |
29 | for data in record:
30 | # 기록 분리
31 | oper, *args = data.split()
32 |
33 | # uid 와 name 파싱
34 | uid = args[0]
35 | if len(args) > 1:
36 | name = args[1]
37 |
38 | # 명령에 따라 처리
39 | if oper == "Enter":
40 | # 입장시 테이블에 이름 추가 및 입장 기록 남기기
41 | table[uid] = name
42 | records.append((True, uid))
43 |
44 | elif oper == "Leave":
45 | # 퇴장시 기록에 퇴장 기록 남기기
46 | records.append((False, uid))
47 |
48 | elif oper == "Change":
49 | # 닉네임 변경하기
50 | table[uid] = name
51 |
52 | # 기록에 따라 변경된 닉네임으로 채팅기록 남기기
53 | for sign, uid in records:
54 | if sign:
55 | sentence = f"{table[uid]}님이 들어왔습니다."
56 | else:
57 | sentence = f"{table[uid]}님이 나갔습니다."
58 | answer.append(sentence)
59 |
60 | return answer
61 |
62 | case1 = ["Enter uid1234 Muzi", "Enter uid4567 Prodo","Leave uid1234","Enter uid1234 Prodo","Change uid4567 Ryan"]
63 | # ["Prodo님이 들어왔습니다.", "Ryan님이 들어왔습니다.", "Prodo님이 나갔습니다.", "Prodo님이 들어왔습니다."]
64 | print(solution(case1))
--------------------------------------------------------------------------------
/Programmers/요격 시스템/dohyun.py:
--------------------------------------------------------------------------------
1 | def solution(targets):
2 | """
3 |
4 | 풀이시간
5 | - 약 1시간 20분 소요
6 |
7 | 접근법
8 | - 다음 미사일의 start point 가 현재 미사일의 end point 보다 멀리 있으면 따로 요격 시켜야 함
9 | - 따라서 다음 미사일의 start point 가 현재 미사일의 end point 보다 작으면 묶어서 요격 가능
10 | - 핵심은 end_point 를 기준으로 정렬해야 함 (예제를 통해 파악)
11 |
12 | 회고
13 | - 왜인지 모르겠지만 풀이가 정돈 되지 않은 느낌..?
14 | - 원래는 리스트에서 인덱스로 접근하는 대신 요소를 제거하는 안 좋은 습관(?)이 있었는데 조금은 고쳐진것 같음
15 |
16 | """
17 |
18 | targets.sort(key = lambda x:x[1])
19 | answer = 1
20 | start_idx = 0
21 |
22 | while start_idx < len(targets)-1:
23 | step = 1
24 |
25 | while targets[start_idx][1] > targets[start_idx + step][0]:
26 | step += 1
27 | if start_idx + step >= len(targets):
28 | answer -= 1
29 | break
30 |
31 | start_idx += step
32 | answer += 1
33 |
34 | return answer
--------------------------------------------------------------------------------
/Programmers/요격 시스템/jisu.py:
--------------------------------------------------------------------------------
1 | def solution(targets):
2 |
3 | targets.sort(key = lambda x : (x[1], x[0])) # 미사일 범위 끝 기준으로 정렬 후 앞에서 부터 접근
4 | bullet = 0
5 | before_end = 0
6 | for target in targets:
7 | if target[0] < before_end: # 현재 바라보고 있는 미사일의 시작 부분이 이전 미사일의 끝 부분보다 작다면
8 | continue # 스플래쉬로 터짐
9 | else: # 안겹치면
10 | before_end = target[1]
11 | bullet += 1 # 한 발 더 쏴야함
12 | return bullet
13 |
14 | case1 = [[4,5],[4,8],[10,14],[11,13],[5,12],[3,7],[1,4]] # 3
15 | case2 = [[0, 4], [1, 2], [1, 3], [3, 4]] # 2
16 | case3 = [[0, 4], [0, 1], [2, 3]] # 2
17 | print(solution(case1))
18 | print(solution(case2))
19 | print(solution(case3))
--------------------------------------------------------------------------------
/Programmers/요격 시스템/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 시간복잡도: O(nlogn)
3 | 풀이시간: 35분
4 | targets의 길이가 500,000인 걸보고, 최대 O(nlogn)으로 해결해야겠다고 생각했음.
5 | 전형적인 그리디 문제. 백준의 회의실 배정, 강의실 배정과 유사해서 정렬 및 그리디를 생각함
6 |
7 | 1. 끝지점을 기준으로 오름차순 정렬해준다.
8 | 2. targets(폭격 미사일 리스트)의 원소를 하나씩 확인하면서 요격미사일 설치지점이 s(시작지점)보다 작은지 확인한다.
9 | 3. 이전에 설치한 요격 미사일이 현재 확인하고 있는 폭격 미사일의 시작지점보다 작다면 해당 폭격 미사일을 요격할 수 없기 때문에
10 | - 미사일을 추가로 설치한다.
11 | - 요격 미사일의 위치를 현재 폭격 미사일의 끝지점으로 갱신한다.
12 | """
13 | def solution(targets):
14 | answer = 0
15 | targets.sort(key=lambda x: x[1]) # e를 기준으로 오름차순 정렬
16 | missile = -1 # s, e는 0이상이니까 -1로 초기화
17 |
18 | for s, e in targets: # 폭격 미사일의 s,e 좌표
19 | if missile < s: # 이전 미사일로 현재 폭격 미사일을 요격할 수 없으면
20 | answer += 1 # 미사일 추가
21 | missile = e-0.1 # 요격 미사일은 실수인 x좌표에서 발사가능(s와 e에서 발사하는 요격미사일로는 요격할 수 없음)
22 | return answer
23 |
24 |
25 | """
26 | [[4,5],[4,8],[10,14],[11,13],[5,12],[3,7],[1,4]] -> 3
27 | [[0, 4], [1, 2], [1, 3], [3, 4]] - >2
28 | [[0, 4], [0, 1], [2, 3]] -> 2
29 | """
30 | targets = [[0, 4], [1, 2], [1, 3], [3, 4]]
31 | print(solution(targets))
--------------------------------------------------------------------------------
/Programmers/요격 시스템/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | [초기 아이디어]
3 | 1. 단위 구간 (i, i+1) 마다 폭격 미사일 개수를 구해서, 많은 곳부터 요격
4 | 2. 요격한 미사일의 개수가 전체 폭격 미사일 개수와 같아질 때까지 요격 => 요격이 끝난 후 요격 개수를 답으로 제출
5 | => 이 아이디어의 문제 : 요격하고 나면, 요격한 폭격 미사일마다 그 구간을 추적해 개수를 0으로 초기화해주어야 하는 과정이 복잡함
6 |
7 | - 따라서 [블로그](https://velog.io/@damin1025/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9A%94%EA%B2%A9-%EC%8B%9C%EC%8A%A4%ED%85%9C)를 참고해 풀이
8 |
9 | [접근 방법]
10 | 그리디 알고리즘
11 | 1. 폭격이 빨리 끝나는 순서대로 (e 기준으로) 오름차순 정렬
12 | 2. 정렬된 폭격 미사일을 쭉 돌면서, s가 e보다 앞에 있으면 같이 요격 가능 (=count를 올리지 않음)
13 | 3. s가 e를 넘는 순간이 오면, 이전 요격이 끝나며 새로운 요격을 시작
14 | 4. 새로운 요격을 시작할 때마다 count => 이 count한 개수를 답으로 제출!
15 |
16 | """
17 |
18 |
19 | def solution(targets):
20 | answer = 0
21 | targets.sort(key=lambda x: x[1]) # e 기준 오름차순 정렬
22 | trgt_in = 0
23 |
24 | for s, e in targets:
25 | if trgt_in <= s: # 이전 요격이 끝나고 새로운 요격을 할 수 있을 때
26 | answer += 1
27 | trgt_in = e
28 |
29 | return answer
30 |
--------------------------------------------------------------------------------
/Programmers/이모티콘 할인행사/yerim.py:
--------------------------------------------------------------------------------
1 | def solution(users, emoticons):
2 | """
3 | - 소요 시간: 33분 풀이하다 감이 안잡혀서 풀이 확인 .. 문제 이해부터 오래 걸렸고, 이해 후 그대로 구현해보려 했으나 구현도 잘 안됨
4 | - 접근법 ([참고 글](https://velog.io/@top1506/2023-KAKAO-%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98-%ED%95%A0%EC%9D%B8%ED%96%89%EC%82%AC-python))
5 | - 문제 조건 확인 시, 입력값 범위가 매우 적다 => 완전 탐색으로 가능
6 | - backtracking을 활용해 풀이
7 | """
8 | sales = [10, 20, 30, 40]
9 | check = [0] * len(emoticons)
10 | answer = []
11 |
12 | def backtracking(depth):
13 | if depth == len(emoticons):
14 | count = 0
15 | result = 0
16 | for rate, price in users:
17 | sum = 0
18 | for i in range(len(check)):
19 | value = emoticons[i] - int(emoticons[i] * (check[i] / 100))
20 | if rate <= check[i]: # 사용자의 기준 할인율(rate)보다 크거나 같으면 구매
21 | sum += value
22 | if sum >= price: # 구매 가격 합이 사용자의 기준 가격(price)을 넘어서면
23 | count += 1 # 이모티콘 플러스 가입
24 | result -= sum
25 | result += sum
26 | answer.append([count, result])
27 | return
28 |
29 | for i in range(len(sales)):
30 | check[depth] = sales[i]
31 | backtracking(depth + 1)
32 | check[depth] = 0
33 |
34 | backtracking(0)
35 | answer.sort(key=lambda x: (x[0], x[1]))
36 | return answer[-1]
37 |
--------------------------------------------------------------------------------
/Programmers/전화번호 목록/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분
5 |
6 | 접근법
7 | - N = 100만 -> O(NlogN) / O(N)
8 | - O(N) 정도의 시간복잡도로 알고리즘이 종료되려면 정렬을 해놓아야 한다고 판단
9 | - 정렬이 되어있으면 이전 전화번호가 다음 전화번호의 접두어인지만 확인하면 됨!
10 |
11 | 회고
12 | - 예외 케이스를 생각하다가 너무 복잡한거 같다는 생각이 들면 단순하게 다시 생각해보기
13 | - 처음에는 정렬하고 특정 구간을 나눠서 문자열의 길이를 기준으로 다시 정렬을 해야하나? 라고 복잡하게 생각했음
14 | - 근데 너무 복잡해서 일단 그냥 정렬만해서 돌려보자! 하니까 됨
15 | - 생각해보니 단순히 정렬만 하면 문자열의 길이는 신경을 안써도 됐음 (길이 대소비교를 만족안하면 알아서 조건문에서 탈락하고, 만족하면 정렬이 되어있으니까 문제 없음)
16 |
17 | """
18 |
19 | def solution(phone_book):
20 | phone_book.sort() # 전화번호 목록 정렬
21 |
22 | for i in range(1, len(phone_book)):
23 | prev = phone_book[i-1]
24 | curr = phone_book[i]
25 | if curr[:len(prev)]==prev: # 접두어 확인
26 | return False
27 |
28 | return True
29 |
30 | """
31 | ["119", "97674223", "1195524421"] -> false
32 | ["123", "456", "789"] -> true
33 | ["12", "123", "1235", "567", "88"] -> flase
34 | """
--------------------------------------------------------------------------------
/Programmers/전화번호 목록/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023-08-20 12:49
3 |
4 | N은 1,000,000 이하이므로 O(NlogN)안에 해결해야한다.
5 |
6 | phone_book에 대해 정렬을 수행하면 아스키 코드 순 -> 짧은 순으로 정렬이 되므로,
7 | 만약 한 번호가 다른 번호의 접두어라면, i-1번째 인덱스의 번호가 i번째 번호의 접두어일 것이다.
8 |
9 | 풀이 완료 : 2023-08-20 13:00 (풀이 시간 : 11분)
10 | '''
11 | from typing import List
12 |
13 | def solution(phone_book: List[str]) -> bool:
14 | phone_book.sort() # 맨 앞 글자의 아스키 코드 오름차순 -> 짧은 순으로 정렬
15 | for i in range(1, len(phone_book)): # 만약 한 번호가 다른 번호의 접두어라면,
16 | if phone_book[i].startswith(phone_book[i-1]): # i-1번째 인덱스의 번호가 i번째 번호의 접두어일 것이다.
17 | return False
18 |
19 | return True
20 |
21 | def main() -> None:
22 | case1 = ["119", "97674223", "1195524421"]
23 | case2 = ["123","456","789"]
24 | case3 = ["12","123","1235","567","88"]
25 |
26 | print(solution(case1)) # false
27 | print(solution(case2)) # true
28 | print(solution(case3)) # false
29 |
30 | main()
--------------------------------------------------------------------------------
/Programmers/전화번호 목록/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 10분
3 |
4 |
5 | - phone_book의 길이: 1 이상 1,000,000 이하
6 | - 각 전화번호의 길이: 1 이상 20 이하
7 | - 같은 전화번호가 중복해서 들어있지 않다.
8 |
9 |
10 | 1) 정렬 후 현재 확인하고 있는 번호가 다음 번호의 접두어인지 확인하는 방법
11 | 2) 해시를 이용해 특정 전화번호의 접두어가 다른 번호인지 확인하는 방법
12 |
13 | <시간 복잡도>
14 | 1) O(nlogn)
15 | 2) O(nm): n은 전화번호의 총 개수, m은 전화번호의 길이로 최대 20
16 |
17 | <기타>
18 | 이 외에 트라이로도 풀이 가능
19 | """
20 |
21 | from typing import List
22 |
23 | # 정렬로 풀기
24 | def solution(phone_book: List) -> bool:
25 | # 전화번호를 사전순으로 정렬
26 | phone_book.sort()
27 |
28 | for i in range(len(phone_book) - 1):
29 | # 다음 번호 비교 -> 접두어인지 확인
30 | if phone_book[i+1].startswith(phone_book[i]): # 접두어인 경우
31 | return False
32 | return True
33 |
34 |
35 | # 해시로 풀기
36 | def solution(phone_book: List) -> bool:
37 | phone_hash = {} # 전화번호 접두사를 저장할 해시
38 |
39 | # 각 전화번호의 접두사를 해시에 저장
40 | for number in phone_book:
41 | phone_hash[number] = True
42 |
43 | for number in phone_book:
44 | for i in range(1, len(number)):
45 | prefix = number[:i]
46 | if prefix in phone_hash: # 접두어인 경우
47 | return False
48 | return True
49 |
50 |
51 | # 테스트 케이스
52 | test_case1 = ["119", "97674223", "1195524421"] # 출력: False
53 | test_case2 = ["123","456","789"] # 출력: True
54 | test_case3 = ["12","123","1235","567","88"] # 출력: False
55 | test_case4 = ["456", "467"] # 출력: True
56 | test_case5 = ["1195524421", "97674223", "119"] # 출력: False
57 |
58 | print(solution(phone_book=test_case5))
--------------------------------------------------------------------------------
/Programmers/정수 삼각형/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023.08.08 23:32
3 |
4 | - n층의 삼각형에서 숫자의 개수는 (2^n)-1 개이다.
5 | - 따라서 모든 경우의 수 중 최대값을 구하고자 하면 나올 수 있는 경우의 수는 ((2^n)-1)! -> 여기서 완전탐색으로 최대값을 찾기에는 너무 연산량이 많다.
6 | - 가장자리에 있는 수들은 하나의 수가 더해지고, 안쪽의 수 들은 두 경우의 수 중 큰 경우의 수를 받을 것이다. 그러면 큰 경우만 받아서 저장하자(dp)
7 | - 그러면 가장 마지막 층에서 가장 큰 원소만 구하면 거쳐간 숫자의 최대값을 구할 수 있다.
8 | - 사실 dp 문제에서 많이 본 유형이었기 때문에 바로 떠올릴 수 있었다.
9 |
10 | 풀이 완료 : 2023.08.09 00:01
11 | '''
12 |
13 | from typing import List
14 |
15 | def solution(triangle : List[List]) -> int:
16 |
17 | for row in range(1, len(triangle)):
18 | for col in range(row+1): # 한 row는 row개의 수를 가짐
19 | if col == 0: # 좌측 가생이에 있는 수의 경우
20 | triangle[row][col] += triangle[row-1][col] # 좌측 위 숫지(같은 col)
21 | elif col == row: # 우측 가생이에 있는 수의 경우
22 | triangle[row][col] += triangle[row-1][col-1] # 우측 위 숫자(col-1)
23 | else: # 그 외의 나머지 수의 경우
24 | triangle[row][col] += max(triangle[row-1][col-1], triangle[row-1][col]) # 좌측 위 숫자나 우측 위 숫자 중에 큰 거 더한 경우를 선택
25 |
26 | return max(triangle[-1]) # 그러면 가장 마지막 층에서 가장 큰 원소가 거쳐간 숫자의 최대값이다.
27 |
28 | def main():
29 | case1 = [[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]]
30 | case2 = [[7]]
31 | case3 = [[7], [3, 8]]
32 | print(solution(case1)) # 30
33 | print(solution(case2)) # 7
34 | print(solution(case3)) # 15
35 |
36 | main()
--------------------------------------------------------------------------------
/Programmers/주식가격/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - N 10만 이하 -> O(NlogN)
8 | - 이전에 풀었던 문제에서 스택으로 시간 여행을 하던게 기억남! 큐로도 구현은 할 수 있을 것 같다
9 | - 를 시도하다가 더 과거의 인덱스도 저장해야하는 등 생각보다 복잡함에 포기...
10 |
11 | - 답지 풀이
12 | - 단순 이중반복문으로 구현
13 | - 비슷하게 생각은 해본 풀이지만 O(N^2) 풀이라 접음 -> 왜 통과하는거지 ..???
14 |
15 | 회고
16 | - 상수시간을 최대한 줄이는 문제도 존재하는듯하다
17 | - 빠르게 구현할수있는 풀이가 생각난다면 우선 구현하여 제출해보는 것도 나쁘지 않은 전략인듯 함
18 |
19 | """
20 |
21 | def solution(prices):
22 | answer = [0] * len(prices) # 결과를 저장할 리스트 초기화
23 |
24 | for i in range(len(prices)):
25 | for j in range(i+1, len(prices)): # 이후의 가격들을 비교
26 | # 현재 가격보다 가격이 떨어진 경우
27 | if prices[i] > prices[j]:
28 | answer[i] = j - i # 현재 시간부터 떨어진 시간까지의 차이 저장
29 | break # 떨어진 경우에는 더 이상 계산하지 않음
30 |
31 | # 가격이 끝까지 떨어지지 않은 경우
32 | if not answer[i]:
33 | answer[i] = len(prices) - i - 1 # 끝까지 남은 시간 차만큼 더해줌
34 |
35 | return answer
--------------------------------------------------------------------------------
/Programmers/주식가격/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-10-01 13:54
3 |
4 | #### 제한 사항
5 | - prices의 길이는 100,000 이하이므로 O(NlogN) 이하의 알고리즘을 설계해야 한다.
6 |
7 | #### 풀이
8 | - i번째 요소부터 모든 원소를 비교하는 방법으로 하면 시간 초과가 날 것
9 |
10 | 풀이 중단 : 2023-10-01 14:54 (1시간 경과)
11 |
12 | #### 풀이 참고
13 | - 스택을 활용, 스택에 떨어지지 않는 부분까지의 인댁스를 담고, 증가하지 않는 부분의 인덱스의 차를 활용한다.
14 | - [1, 2, 3, 2, 3]의 경우 0번째부터 떨어지지 않는 부분까지 스택에 push -> stack : [0, 1, 2]
15 | - idx(3)번째의 2는 stack[-1](2)번째 원소보다 작으므로, idx-stack[-1] = 3 - 2 = 1초 동안 떨어지지 않음
16 | - 다음 떨어지지 않는 부분까지인 3번째 4번째 인덱스까지 스택에 담으면 최종적으로 [0, 1, 3, 4]
17 | - 스택에 남아있는 값들은 끝날 때까지 가격이 떨어지지 않은 것이다.
18 | - 따라서 해당 idx의 떨어지지 않는 기간은 len(prices)-1 초에서 해당 idx를 뺀 기간이다.
19 |
20 | 풀이 완료 : 2023-10-01 15:25 (1시간 31분 소요)
21 |
22 | """
23 | from typing import List
24 |
25 |
26 | def solution(prices: List[int]) -> List[int]:
27 | answer = [0 for _ in range(len(prices))]
28 | stack = []
29 |
30 | for idx, price in enumerate(prices): # 가격이 떨어지지 않는 부분까지 stack에 인덱스 담기
31 | while stack and price < prices[stack[-1]]: # 값이 떨어지는 경우
32 | j = stack.pop() # stack에 담긴 인덱스를 순서대로 pop해
33 | answer[j] = idx - j # 어디서부터 떨어졌는지를 인덱스의 차를 통해 파악
34 |
35 | stack.append(idx) # 떨어지는 부분 계산 끝났으면 다시 떨어지지 않는 부분 담기
36 |
37 | while stack: # 끝날 때까지 값이 떨어지지 않은 애들
38 | j = stack.pop() # stack의 값들은 해당 애들의 인덱스 값이므로
39 | answer[j] = len(prices) - 1 - j # 끝날 때와 인덱스의 차가 떨어지지 않은 기간
40 |
41 | return answer
42 |
43 |
44 | def main() -> None:
45 | case1 = [1, 2, 3, 2, 3]
46 |
47 | print(solution(case1)) # [4, 3, 1, 1, 0]
48 |
49 |
50 | main()
51 |
--------------------------------------------------------------------------------
/Programmers/주차 요금 계산/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 37분
5 |
6 | 접근법
7 | - records 의 길이가 1000 이하 -> 시간 복잡도 고려 X
8 | - 시간:분 포맷을 분 단위로 변환해야 편할 듯함
9 | - 출차 기록이 없는 친구들이 조금 까다로울 듯 한데, IN 개수와 OUT 개수가 안맞으면 23:59 출차를 추가하면 될듯
10 | - 이를 차량번호의 출차기록의 length 가 홀수인지 체크함으로서 구함
11 | - 딕셔너리를 만들어 키값으로 차량번호를 넣으면 키값으로 정렬된 딕셔너리를 통해 반복문으로 풀 수 있을 듯함
12 |
13 | 회고
14 | - 시각이 기본적으로 정렬되어 있어서 단순 append 나 출차 기록 예외처리 등이 수월했던 것 같음
15 | - 중간중간 잔고장(?) 이 나서 시간이 조금 더 오래걸렸던 것 같은데 해볼만하다고 느낄수록 꼭 집중 놓지말기
16 |
17 | """
18 |
19 | import math
20 |
21 | def solution(fees, records):
22 | base_time, base_fee, per_time, per_fee = fees[0], fees[1], fees[2], fees[3]
23 | answer = []
24 |
25 | records_dict = {}
26 |
27 | for record in records: # 입력값 전처리
28 | time, car_num, inout = record.split()
29 | time = int(time[:2]) * 60 + int(time[3:]) # 시간을 분단위 포맷으로 변환
30 |
31 | if car_num in records_dict: # 레코드를 dictionary 형태로 바꾼 후 value 로는 time 을 입력
32 | records_dict[car_num].append(time)
33 | else:
34 | records_dict[car_num] = [time]
35 |
36 | for key, item in sorted(records_dict.items()): # 차량번호 순으로 정렬된 값을 뱉어야하므로 정렬
37 | if len(item)%2 == 1: # 출차기록이 없으면 (즉 길이가 홀수)
38 | item.append(23*60 + 59) # 23:59분 출차기록 추가
39 |
40 | cum_time = 0
41 | for idx in range(0, len(item), 2): # 이미 정렬되어 있으므로 입차, 출차를 해당 반복문으로 구함
42 | cum_time += (item[idx+1] - item[idx])
43 |
44 | if cum_time <= base_time: # 누적시간이 기본시간보다 작으면 기본요금 부과
45 | answer.append(base_fee)
46 | else: # 그렇지 않으면 원래 계산 공식대로 부과
47 | fee = base_fee + math.ceil((cum_time - base_time) / per_time) * per_fee
48 | answer.append(fee)
49 |
50 | return answer
--------------------------------------------------------------------------------
/Programmers/최고의 집합/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 1시간
5 |
6 | 접근법
7 | - 사전 시간 복잡도 접근을 어떻게 해야할지 모르겠음..!!
8 | - 최고의 집합이 존재하지 않는 경우는 n > s 인 경우밖에 없음
9 | - 처음에는 모든 경우의 수를 구해서 곱을 갱신하는 방식으로 하려고 했음 -> 1시간 넘겨서 그냥 힌트보고 풀이
10 | - 가장 큰 같은 숫자끼리 곱하는 것이 중요
11 | - n * n = n^2, (n-1)(n+1) = n^2-1, (n-2)(n+2) = n^2 - 4 , ...
12 | - n * n * n = n^3, n-1 * n * n+1 = n^3-n, ...
13 | - 위와 같이 합이 n 이면 같은 숫자끼리 곱하는 것으로 가장 큰 값을 구할 수 있다!
14 | - 몫과 나머지 연산을 통해 해결할 수 있음
15 | - 몫으로 모든 값들을 통일
16 | - 이후 순서를 고려해 나머지를 뒤에서부터 더해줌
17 |
18 | 회고
19 | - 예시를 여러 개 들어보면서 같은 숫자를 곱하는 것, 큰 숫자를 곱하는것이 중요하다는 것은 눈치챘지만 몫과 나머지 연산을 생각 못했음
20 | - 예시에서 규칙이 발생하면 그것에 집중해서 풀이하는 연습도 필요할 것 같다
21 |
22 | """
23 |
24 | def solution(n, s):
25 | if n > s:
26 | return [-1] # 최고의 집합이 존재하지 않는 경우
27 |
28 | quotient, remainder = divmod(s, n) # 몫과 나머지
29 |
30 | answer = [quotient] * n # 몫으로 모든 배열을 채우면 가장 큰 숫자의 평균들로 채울 수 있음
31 |
32 | for i in range(n-1, n-1 - remainder, -1): # 순서를 고려해 뒤에서부터 나머지를 하나씩 더해줌
33 | answer[i] += 1
34 |
35 | return answer
36 |
37 |
38 | print(solution(n=2, s=9)) # [4, 5]
39 | print('----------')
40 | print(solution(n=2, s=1)) # [-1]
41 | print('----------')
42 | print(solution(n=2, s=8)) # [4, 4]
43 | print('----------')
44 | print(solution(n=3, s=5)) # [1, 2, 2]
--------------------------------------------------------------------------------
/Programmers/최고의 집합/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023-08-21 11:11
3 |
4 | 1 <= n <= 10,000 이므로 O(NlogN)으로 구현해야 한다.
5 |
6 | 중복집합임에 주의, 각 집합의 원소는 중복될 수 있음
7 | 최고 집합이 존재하지 않는 경우는 S가 합으로 만들어지지 않는 집합 => n보다 s가 더 작은 경우
8 |
9 | 최대한 S를 큰 수로 토막 내는 것이 각 원소의 곱이 최대가 되는 집합이다.
10 | 1. S를 N으로 나눈 몫을 m이라고 할때 집합에 m을 추가
11 | 2. S-=m, N-=1
12 | 3. N개의 수가 모일 때까지 반복
13 | 4. 정렬 후 반환
14 |
15 | 풀이 완료 : 2023-08-21 11:28 (풀이 시간 : 17분)
16 | '''
17 | from typing import List
18 |
19 | def solution(n: int, s: int) -> List:
20 |
21 | answer:List = list()
22 |
23 | if n > s: # 예외 : 최고의 집합이 만들어지지 않는 경우
24 | return [-1]
25 |
26 | while n > 0: # 아래 과정을 통해 집합의 원소가 N개가 될 때까지 반복
27 | m = s//n # S를 최대한 큰 수로 토막내야 한다.
28 | answer.append(m)
29 | n -= 1
30 | s -= m
31 |
32 | return sorted(answer) # 오름차순으로 정렬된 리스트를 반환
33 |
34 |
35 | def main() -> None:
36 | case1 = [2, 9]
37 | case2 = [2, 1]
38 | case3 = [2, 8]
39 |
40 | print(solution(*case1))
41 | print(solution(*case2))
42 | print(solution(*case3))
43 |
44 | main()
--------------------------------------------------------------------------------
/Programmers/최고의 집합/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 |
5 | - 자연수의 개수 n: 1 이상 10,000 이하의 자연수
6 | - 모든 원소들의 합 s: 1 이상, 100,000,000 이하의 자연수
7 |
8 |
9 | - s가 최대 1억 -> 직접 모든 경우의 수를 구하는 것은 불가능
10 | - 원소의 곱이 최대가 되려면 예제처럼 각 수들간의 차이가 작은 수들끼리 곱해야함
11 | - n 제한이 1만개이기 때문에 n을 활용(또한, n이 자연수이기 때문에 항상 s가 n보다 커야함)
12 |
13 | 위 세가지 조건들을 생각하며 몇 가지 예제를 통해 공통점을 발견
14 | - s//n를 시작점으로 n-(s%n)개
15 | - (s//n)+1는 시작점으로 부터 (s%n)개
16 |
17 | <시간복잡도>
18 | O(n): 리스트 컴프리헨션을 통해 답을 도출하는 과정에서 발생
19 | """
20 | from typing import List
21 |
22 | def solution(n: int, s: int) -> List:
23 | if n > s: return [-1] # n은 자연수이기 때문에 s보다 크다면 조건에 만족하는 집합을 만들 수 없음
24 | start, remainder = divmod(s, n) # 시작점(몫), 나머지
25 | return [start] * (n-remainder) + [start+1] * remainder # 시작점은 (n-나머지)의 개수, 시작점+1은 나머지의 개수
26 |
27 |
28 | """
29 | 정확성 테스트
30 | 테스트 1 〉 통과 (0.03ms, 10.3MB)
31 | 테스트 2 〉 통과 (0.05ms, 10.3MB)
32 | 테스트 3 〉 통과 (0.05ms, 10.2MB)
33 | 테스트 4 〉 통과 (0.04ms, 10.4MB)
34 | 테스트 5 〉 통과 (0.01ms, 10.2MB)
35 | 테스트 6 〉 통과 (0.02ms, 10.3MB)
36 | 테스트 7 〉 통과 (0.01ms, 10.3MB)
37 | 테스트 8 〉 통과 (0.01ms, 10.3MB)
38 | 테스트 9 〉 통과 (0.03ms, 10.4MB)
39 | 테스트 10 〉통과 (0.06ms, 10.4MB)
40 | 테스트 11 〉통과 (0.05ms, 10.5MB)
41 | 테스트 12 〉통과 (0.03ms, 10.3MB)
42 | 테스트 13 〉통과 (0.05ms, 10.4MB)
43 | 테스트 14 〉통과 (0.00ms, 10.2MB)
44 |
45 |
46 | 효율성 테스트
47 | 테스트 1 〉 통과 (0.07ms, 10.5MB)
48 | 테스트 2 〉 통과 (0.07ms, 10.3MB)
49 | 테스트 3 〉 통과 (0.07ms, 10.5MB)
50 | 테스트 4 〉 통과 (0.09ms, 10.6MB)
51 | 테스트 5 〉 통과 (0.10ms, 10.7MB)
52 | 테스트 6 〉 통과 (0.00ms, 10.1MB)
53 | """
--------------------------------------------------------------------------------
/Programmers/최고의 집합/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 최고의 집합
3 | https://school.programmers.co.kr/learn/courses/30/lessons/12938
4 |
5 | 풀이시간
6 | 21:46 ~ 22:28 (38분)
7 |
8 | 문제 조건
9 | 자연수의 개수 : 1 ~ 10,000 (n)
10 | 모든 원소들의 합 : 1 ~ 100,000,000
11 |
12 | 시간 복잡도 : O(n)
13 |
14 | 접근법
15 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 단순 구현(수학)
16 |
17 | - 예외처리 구간(최고의 집합이 존재하지 않는 경우) : 모든 원소의 합이 원소의 개수보다 작을 경우에는 최고의 집합이 존재하지 않음
18 |
19 | - N개의 자연수의 중복을 허용하는 집합에서 S라는 모든 원소의 합을 값을 가지는 케이스 중에서 원소들의 곱이 최대가 되는 경우
20 | - 최대한 항들이 커야하기 때문에 모든 항들이 균일한 값을 가져야하며 모두 같은 값을 가질 수 없을 때는 (오름차순 정렬을 만들기 위해) 뒤의 원소부터 1씩 더 클 수 있게 분배합니다.
21 | - divmod를 사용해서 s//n과 s%n을 연산하고 몫은 해당 값의 개수만큼 answer 리스트를 만들고 나머지의 크기만큼 뒤에서 앞의 순서로 각 원소에 1을 더해준다.
22 | """
23 | from typing import List
24 |
25 |
26 | def solution(n: int, s: int) -> List[int]:
27 | # 최고의 집합이 존재하지 않는 경우
28 | # 모든 원소의 합이 원소의 개수보다 작을 경우에는 최고의 집합이 존재하지 않음
29 | if n > s:
30 | return [-1]
31 |
32 | # s를 n으로 나눴을 때 몫과 나머지 연산
33 | n_div_value, m = divmod(s, n)
34 |
35 | # n개만큼 몫을 answer에 저장하기
36 | answer = [n_div_value for _ in range(n)]
37 |
38 | # 뒤에서 부터 나머지 만큼 +1씩 원소에 더해주기
39 | for idx in range(-m, 0):
40 | answer[idx] += 1
41 |
42 | return answer
43 |
44 |
45 | print(solution(2, 9))
46 | print(solution(2, 1))
47 | print(solution(2, 8))
48 |
--------------------------------------------------------------------------------
/Programmers/타겟 넘버/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023.08.09 13:03
3 |
4 | - 순서를 바꾸지 않아도 된다 -> 덧셈 뺄셈 연산자만 바꿔가며 경우의 수를 판단
5 | - 그리디한 접근은 어려울 것 같고(어차피 다 탐색해야함), 주어지는 수의 개수가 많지 않아서(20개) 완전탐색(dfs)으로 구현할 수 있을듯 함
6 | - 모든 수를 순서대로 탐색해야하므로 가지 치기는 없고, 그냥 부호만 바꿔가며 가지를 뻗어나가기만 하면 될 것 같다.
7 |
8 | 풀이 완료 : 2023.08.09 13:22
9 | '''
10 |
11 | from typing import List
12 |
13 |
14 | result = 0
15 |
16 | def dfs(idx: int, numbers: List[int], target: int, total: int):
17 | global result
18 |
19 | if idx == len(numbers): # 모든 수의 탐색이 끝난 경우
20 | result += 1 if total == target else 0 # 해당 경우의 수의 결과가 target number이면 최종 결과에 +1
21 | return # 이는 재귀의 종료 조건임
22 |
23 | dfs(idx+1, numbers, target, total+numbers[idx]) # idx번째를 더하고 idx+1번째 탐색 시작
24 | dfs(idx+1, numbers, target, total-numbers[idx]) # idx번째를 빼고 idx+1번째 탐색 시작
25 |
26 |
27 | def solution(numbers: List[int], target: int) -> int:
28 | global result
29 |
30 | dfs(0, numbers, target, 0) # 0번 인덱스부터 탐색 시작
31 |
32 | return result
33 |
34 | def main() -> None:
35 | global result
36 | case1 = [[1, 1, 1, 1, 1], 3] # 5
37 | case2 = [[4, 1, 2, 1], 2] # 2
38 |
39 | print(solution(*case1)) # 5
40 | result = 0 # 전역변수 초기화
41 | print(solution(*case2)) # 2
42 |
43 | main()
--------------------------------------------------------------------------------
/Programmers/타겟 넘버/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 20분
3 |
4 | <제한사항>
5 | - 2 <= 숫자 개수(n) <= 20
6 | - n개의 음이 아닌 정수
7 | - 숫자의 순서는 바꾸지 않고, +/- 둘 중에 하나를 사용
8 |
9 | <시간복잡도>
10 | O(2^n): 연산자(+/-)둘 중 하나를 n번 선택하는 경우
11 | - n이 최대 20이기 때문에 2^20 = 1,048,576 연산횟수로 시간 내 통과 가능
12 | """
13 |
14 | from typing import List
15 | def solution(numbers, target):
16 | answer = go([], numbers, target)
17 | return answer
18 |
19 | def go(oper: List, nums: List, goal: int) -> int:
20 | """
21 | oper: 연산자의 개수
22 | nums: 숫자의 개수
23 | goal: 타겟 넘버
24 | """
25 | if len(oper) == len(nums): # 연산자의 개수가 숫자의 개수와 같다면
26 | s = 0 # 합
27 | for i in range(len(nums)):
28 | s += oper[i] * nums[i] # - 또는 +가 붙은 수를 s에 더해줌
29 | if s == goal: # Base condition(타겟 넘버가 되면)
30 | return 1
31 | return 0 # 모든 경우의 수를 확인했지만 타겟 넘버가 될 수 없다면 0
32 |
33 | now = 0 # 타겟 넘버를 만드는 방법의 수
34 | now += go(oper+[-1], nums, goal) # -를 붙이는 경우
35 | now += go(oper+[1], nums, goal) # +를 붙이는 경우
36 | return now
37 |
38 |
39 | """
40 | 테스트 1 〉 통과 (3231.89ms, 10.6MB)
41 | 테스트 2 〉 통과 (3583.76ms, 10.6MB)
42 | 테스트 3 〉 통과 (2.76ms, 10.5MB)
43 | 테스트 4 〉 통과 (11.87ms, 10.5MB)
44 | 테스트 5 〉 통과 (128.74ms, 10.4MB)
45 | 테스트 6 〉 통과 (6.73ms, 10.6MB)
46 | 테스트 7 〉 통과 (3.28ms, 10.6MB)
47 | 테스트 8 〉 통과 (24.54ms, 10.4MB)
48 | """
--------------------------------------------------------------------------------
/Programmers/택배 배달과 수거하기/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 | 택배 배달과 수거하기
3 | https://school.programmers.co.kr/learn/courses/30/lessons/150369
4 |
5 | 접근법
6 | - 물류창고에서 출발할 때 최대한 멀리가는 것이 좋을 것 같다고 생각
7 | - 어차피 멀리있는 집도 배달하러 가야하므로 돌아오는 길에 수거하기 편리
8 | - 즉 멀리 있는 집을 최대한 먼저 배달하고, 돌아올 때도 멀리있는 집 순으로 수거를 빨리 해야함
9 | - 모든 집을 들린다고 코드를 설계한 후 실제로 배달 또는 수거가 있었을 경우에 answer 를 추가하는게 낫겠다!
10 |
11 | - 2시간 가까이 풀었는데 테스트 케이스에서 안 맞아서 레퍼런스 확인 ㅜㅜ
12 | - 역순으로 정렬하는 것보다 뒤에서부터 가는 것이 조금 더 직관적인 것 같아 해당 풀이로 선택
13 | """
14 |
15 | def solution(cap, n, deliveries, pickups):
16 | answer = 0
17 |
18 | deli = 0
19 | pick = 0
20 |
21 | for i in range(n-1, -1, -1):
22 | deli += deliveries[i]
23 | pick += pickups[i]
24 |
25 | while deli > 0 or pick > 0:
26 | deli -= cap
27 | pick -= cap
28 | answer += (i + 1) * 2
29 | return answer
--------------------------------------------------------------------------------
/Programmers/택배 배달과 수거하기/jisu.py:
--------------------------------------------------------------------------------
1 | def solution(cap, n, deliveries, pickups):
2 | d, p = n-1, n-1
3 | min_distance = 0
4 | while deliveries[d] == 0 and d >= 0:
5 | d -= 1
6 | while pickups[p] == 0 and p >= 0:
7 | p -= 1
8 |
9 | while d >= 0 or p >= 0:
10 | min_distance += (max(d, p)+1) * 2
11 |
12 | d_cap = cap
13 | p_cap = cap
14 |
15 | while d_cap > 0 and d >= 0:
16 | if deliveries[d] > d_cap:
17 | deliveries[d] -= d_cap
18 | d_cap = 0
19 | else:
20 | d_cap -= deliveries[d]
21 | deliveries[d] = 0
22 | while deliveries[d] == 0 and d >= 0:
23 | d -= 1
24 |
25 | while p_cap > 0 and p >= 0:
26 | if pickups[p] > p_cap:
27 | pickups[p] -= p_cap
28 | p_cap = 0
29 | else:
30 | p_cap -= pickups[p]
31 | pickups[p] = 0
32 | while pickups[p] == 0 and p >= 0:
33 | p -= 1
34 |
35 | return min_distance
36 |
37 |
38 |
39 | case1 = [4, 5, [1, 0, 3, 1 ,2], [0, 3, 0, 4, 0]]
40 | case2 = [2, 7, [1, 0, 2, 0, 1, 0, 2], [0, 2, 0, 1, 0, 2, 0]]
41 | print(solution(*case1)) # 16
42 | print(solution(*case2)) # 30
--------------------------------------------------------------------------------
/Programmers/택배 배달과 수거하기/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 택배 배달과 수거하기
3 | https://school.programmers.co.kr/learn/courses/30/lessons/150369
4 |
5 | 50분 풀이 후 감을 잡지 못해서 풀이 참조
6 |
7 | 접근법
8 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 그리디
9 |
10 | - 두 리스트가 주어지고 cap 만큼 택배 상자를 제거해나가야한다.
11 | - 하나는 전달하면 사라지고 하나는 회수해야 사라진다.
12 |
13 | 최소 경로 동선은 가장 먼 집부터 문제를 해결하였을 때 구할 수 있다.
14 | 현재까지의 주소지에 택배 전달 + 가능한 수거 모두 하기
15 | """
16 |
17 | def solution(cap, n, deliveries, pickups):
18 | # 역순으로 검사하기
19 | deliveries.reverse()
20 | pickups.reverse()
21 |
22 | answer,sum_of_current_delivery,sum_of_current_pickup = 0,0,0
23 |
24 | # 그리디 접근
25 | # 한 iter씩 검사하며 배송을 해야하는 갯수 + 픽업을 해야하는 갯수를 추가한다.
26 | for idx in range(n):
27 | # (현재 기준)마지막째 주소지의 배송과 픽업 갯수 갱신
28 | sum_of_current_delivery += deliveries[idx]
29 | sum_of_current_pickup += pickups[idx]
30 |
31 | # 만약 배송 혹은 픽업이 가능한 장소가 있다면 해당 위치까지 왕복 거리 수행
32 | while sum_of_current_delivery > 0 or sum_of_current_pickup > 0:
33 | sum_of_current_delivery -= cap
34 | sum_of_current_pickup -= cap
35 | answer += (n - idx) * 2
36 |
37 | return answer
38 |
39 | #1 Case #1
40 | cap,n,deliveries,pickups = 4,5,[1,0,3,1,2],[0,3,0,4,0]
41 | print(solution(cap,n,deliveries,pickups))
42 |
43 | #2 Case #2
44 | cap,n,deliveries,pickups = 2,7,[1, 0, 2, 0, 1, 0, 2],[0, 2, 0, 1, 0, 2, 0]
45 | print(solution(cap,n,deliveries,pickups))
--------------------------------------------------------------------------------
/Programmers/택배 배달과 수거하기/yerim.py:
--------------------------------------------------------------------------------
1 | def solution(cap, n, deliveries, pickups):
2 |
3 | """
4 | - 가장 먼 집부터 배달 및 수거
5 | - 왕복으로 이동해야 햐므로, 배달과 수거를 동시에 해야 최소 이동 가능
6 | - 먼 집에서부터 배달해야 할 상자 개수(to_dlvr), 수거해야 할 상자 개수(to_pick)를 모두 센다.
7 | - 배달 및 수거를 할 때, 물류창고에 들릴 때마다 생기는 최대 수용치(cap)을 모두 쓴다고 가정하며 to_dlvr, to_pick 값을 cap 만큼 빼줌
8 | - to_dlvr, to_pick을 갱신할 때마다 둘 다 0보다 같거나 작으면 트럭에 상자를 더 수용할 여력이 있는 것이므로,
9 | 물류창고에 들리지 않고 그 앞집으로 이동해 추가 배달 및 수거
10 | - 둘 중 한 값이라도 0보다 커지면 최대 수용 한계를 넘어선 것이므로 물류창고에 들림 => 이때 왕복 거리 계산해 answer에 더함
11 |
12 | 참고: https://ddingmin00.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%8C%8C%EC%9D%B4%EC%8D%AC-2023-KAKAO-BLIND-RECRUITMENT-%ED%83%9D%EB%B0%B0-%EB%B0%B0%EB%8B%AC%EA%B3%BC-%EC%88%98%EA%B1%B0%ED%95%98%EA%B8%B0?category=1331896
13 | """
14 |
15 | answer = 0
16 | to_dlvr = 0
17 | to_pick = 0
18 |
19 | for i in range(n - 1, -1, -1):
20 | to_dlvr += deliveries[i]
21 | to_pick += pickups[i]
22 |
23 | while to_dlvr > 0 or to_pick > 0:
24 | to_dlvr -= cap
25 | to_pick -= cap
26 | answer += (i + 1) * 2
27 |
28 | return answer
--------------------------------------------------------------------------------
/Programmers/표현 가능한 이진트리/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 30분 고민해보고 도저히 손도 안 떨어져서 레퍼런스를 참고해서 공부해보자고 생각
5 |
6 | 접근법
7 | - numbers 의 원소의 크기가 10^15 -> 시간복잡도 무조건 고려
8 | - 라고 생각했지만 이진수로 변환을 하기 때문에 생각보다 별로 안될 것이라고 생각
9 | - Python 내장 bin 함수를 이용하면 십진수 값을 이진수 문자열로 돌려줌!
10 |
11 | 회고
12 | - 진짜진짜 도저히 모르겠어서 레퍼런스 풀이를 여러 개 찾아봐도 도저히 모르겠음
13 | - 코딩테스트 문제 풀면서 아예 문제 자체가 낯선 개념인게 처음이라 당황스럽다 ... 자료구조/알고리즘 개념이 없어서 그런가 ...
14 |
15 | """
16 |
17 | import math
18 |
19 | def check_tree(s):
20 | l_s = len(s)
21 | if l_s <= 1:
22 | return s
23 | center = l_s // 2
24 | if s[center] == '1':
25 | return check_tree(s[:center]) + s[center] + check_tree(s[-center:])
26 | else:
27 | return '0' * l_s
28 |
29 | def solution(numbers):
30 | answer = []
31 |
32 | for n in numbers:
33 | s = bin(n)[2:]
34 | l = len(s)
35 | new_l = 2 ** int(math.log2(l) + 1) - 1
36 | s = '0' * (new_l - l) + s
37 | if s == check_tree(s):
38 | answer.append(1)
39 | else:
40 | answer.append(0)
41 |
42 | return answer
--------------------------------------------------------------------------------
/Programmers/표현 가능한 이진트리/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 1시간
3 | - 30분 고민하고 재귀 구현하는 부분에서 자꾸 삑나서 방법만 참고
4 | - 코드 작성하고 디버깅: 30분
5 |
6 | <시간복잡도> : O(N)
7 |
8 |
9 | 1. 이진수로 변환
10 | 2. 포화 이진트리 만들기
11 | 3. 포화 이진트리가 제대로 성립하는지 확인
12 | """
13 | import math
14 |
15 |
16 | def solution(numbers): # numbers: 이진트리로 만들고 싶은 수를 담은 1차원 정수 배열
17 | answer = [] # 이진트리로 표현할 수 있는 수(1), 없는 수(0)
18 | for num in numbers: # 수를 하나씩 확인한다.
19 | bt = make_tree(num) # 포화 이진트리로 만들어준다.
20 | if check(bt): # 정상적인 포화 이진트리라면
21 | answer.append(1) # answer에 1을 추가
22 | else: # 아니라면
23 | answer.append(0) # answer에 0을 추가
24 | return answer
25 |
26 |
27 | def make_tree(num): # 포화 이진트리로 만들기
28 | b = bin(num)[2:] # 이진수로 변환
29 | h = math.ceil(math.log2(len(b) + 1)) # 트리의 높이
30 | size = 2 ** h - 1 # 노드의 개수
31 |
32 | dummy = size - len(b) # 더미 노드의 개수
33 | bt = '0' * dummy + b # 포화 이지트리 크기보다 길이가 짧다면 0(더미)를 앞에 붙여준다. (뒤에 붙여주면 값이 변해서 안됨)
34 |
35 | return bt
36 |
37 |
38 | def check(bt):
39 | if len(bt) <= 1: # 노드의 개수가 1개 이하이면 무조건 포화이진트리임
40 | return True
41 |
42 | mid = len(bt) // 2
43 | left_tree = bt[:mid] # 왼쪽 자식 서브트리
44 | right_tree = bt[mid+1:] # 오른쪽 자식 서브 트리
45 |
46 | root = bt[mid] # 루트 노드
47 | left = left_tree[len(left_tree)//2] # 왼쪽 서브트리의 부모 노드
48 | right = right_tree[len(right_tree)//2] # 오른쪽 서브트리의 부모 노드
49 |
50 | if root == '0' and (left == '1' or right == '1'): # 더미노드인 경우 1인 자식 노드를 가질 수 없음
51 | return False
52 | else:
53 | return check(left_tree) and check(right_tree) # 재귀적으로 왼쪽 서브트리와 오른쪽 서브트리를 확인한다.
54 |
55 |
56 | print(solution([7, 42, 5]))
--------------------------------------------------------------------------------
/Programmers/표현 가능한 이진트리/yerim.py:
--------------------------------------------------------------------------------
1 | """
2 | [프로그래머스 - 표현 가능한 이진트리](https://school.programmers.co.kr/learn/courses/30/lessons/150367)
3 | - 15분 정도 문제 이해에만 힘을 쏟았는데 이해가 안돼서 [정답](https://ddingmin00.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%8C%8C%EC%9D%B4%EC%8D%AC-2023-KAKAO-BLIND-RECRUITMENT-%ED%91%9C%ED%98%84-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9D%B4%EC%A7%84%ED%8A%B8%EB%A6%AC) 참고
4 | - 문제 이해부터 풀이까지 다 너무 어려웠음..
5 | """
6 |
7 | import math
8 |
9 |
10 | def check(num_bin, prev_parent):
11 | """
12 | 이진 트리를 만족하기 위해서는,
13 | 자식 노드 위에 항상 부모 노드가 존재해야 함 => 이를 체크하는 함수
14 | """
15 | mid = len(num_bin) // 2 # 중앙값 기준으로 재귀적으로 확인
16 |
17 | if num_bin:
18 | son = num_bin[mid] == "1"
19 | else: # 더 이상 확인할 노드가 없으면 True 반환
20 | return True
21 |
22 | if son and not prev_parent: # 부모 노드가 없으면 False 반환
23 | return False
24 | else: # mid 앞, 뒤에 대해 재귀적으로 check
25 | return check(num_bin[mid + 1 :], son) and check(num_bin[:mid], son)
26 |
27 |
28 | def sol(num):
29 | if num == 1: # 1은 항상 참
30 | return 1
31 |
32 | # 2진수 변환
33 | num_bin = bin(num)[2:]
34 |
35 | # 이진 포화트리에 맞게 자릿수를 맞춰줘야 함 => 2^n - 1 꼴의 자릿수를 가져야 함
36 | digit = 2 ** (int(math.log(len(num_bin), 2)) + 1) - 1
37 | num_bin = "0" * (digit - len(num_bin)) + num_bin # 자릿수에 맞게 앞에 0을 채워줌
38 |
39 | # 누군가의 부모 노드는 항상 존재해야 함
40 | if num_bin[len(num_bin) // 2] == "1" and check(num_bin, True):
41 | return 1 # 존재하면 1
42 | else:
43 | return 0 # 존재하지 않으면 0
44 |
45 |
46 | def solution(numbers):
47 | answer = []
48 | for num in numbers:
49 | answer.append(sol(num))
50 |
51 | return answer
52 |
--------------------------------------------------------------------------------
/Programmers/피로도/jisu.py:
--------------------------------------------------------------------------------
1 | '''
2 | 풀이 시작 : 2023-08-03 16:50
3 |
4 | - 던전 개수가 최대 8개이므로 뭔 짓을 해도 될듯
5 | - 완전 탐색으로 접근해보자
6 | - permutation으로 모든 순서를 정해서 탐험 가능한 던전 수를 모두 구하는 방식
7 |
8 | 풀이 완료 : 2023-08-03 17:12
9 |
10 | ##### 완전탐색 말고 그리디하게 풀 수는 없을까?
11 | - 최소 피로도와 소모 피로도의 상관 관계를 생각해봤을 떄 그리디한 접근은 어려울 것 같음
12 | - 여러 풀이들을 확인해봤지만 모든 케이스에서 성공한 그리디 풀이는 없었음
13 | '''
14 |
15 | from typing import List
16 | from itertools import permutations
17 |
18 | def solution(k:int, dungeons:List):
19 | max_result = 0
20 |
21 | all_cases = permutations(dungeons) # 던전 탐험 순서의 모든 경우의 수
22 | for case in all_cases: # 하나씩 드가 봅시다
23 | cur_k = k
24 | num_of_clear = 0
25 | breakFlag = False
26 |
27 | for at_list, consumption in case: # 하나의 케이스에서 각 던전 탐색
28 | if cur_k >= at_list: # 다음 던전 탐험이 가능한 경우 (현재 피로도가 던전 최소 피로도 이상인 경우)
29 | cur_k -= consumption # 피로도 차감해주고
30 | num_of_clear += 1 # 클리어
31 | else: # 다음 던전 탐험이 가능하지 않은 경우
32 | continue # 다음 던전은 패스
33 |
34 | if max_result < num_of_clear: # 탐험 가능 최대 던전 수 업데이트
35 | max_result = num_of_clear
36 |
37 | return max_result # 모든 경우에서 최대 던전 수 반환
38 |
39 | def main():
40 | case1 = [80, [[80,20],[50,40],[30,10]]] # 3
41 | print(solution(*case1))
42 |
43 | main()
--------------------------------------------------------------------------------
/Programmers/피로도/sumin3.py:
--------------------------------------------------------------------------------
1 | """
2 | # 백트래킹(재귀)을 이용한 풀이
3 | - 풀이시간: 15분
4 | - 시간복잡도: O(2 ** N) -> 최대 256번 연산으로 시간 내에 통과가능
5 | - 던전의 개수를 n이라고 하면, 각 던전에 대해서 선택/비선택의 2가지 경우가 있기 때문.
6 | """
7 | from typing import List
8 |
9 | answer = 0
10 | n = 0
11 | visited = []
12 |
13 |
14 | def go(k:int, cnt:int, dungeons:List) -> None:
15 | global answer
16 | if cnt > answer: # 방문할 수 있는 던전의 개수가 이전까지보다 더 많다면 갱신
17 | answer = cnt
18 |
19 | for i in range(n):
20 | if k >= dungeons[i][0] and not visited[i]:
21 | visited[i] = True # 방문처리
22 | go(k - dungeons[i][1], cnt+1, dungeons) # 현재 피로도 - 소모 피로도
23 | visited[i] = False # 백트래킹
24 |
25 | def solution(k:int, dungeons:List) -> int:
26 | global n, visited
27 | n = len(dungeons) # 던전의 개수
28 | visited = [False] * n # 던전 방문처리 배열
29 | go(k, 0, dungeons)
30 | return answer
31 |
32 |
33 | """
34 | 테스트 1 〉 통과 (0.03ms, 10.1MB)
35 | 테스트 2 〉 통과 (0.03ms, 10.2MB)
36 | 테스트 3 〉 통과 (0.02ms, 10.2MB)
37 | 테스트 4 〉 통과 (0.28ms, 10.3MB)
38 | 테스트 5 〉 통과 (1.08ms, 10.2MB)
39 | 테스트 6 〉 통과 (2.82ms, 10.2MB)
40 | 테스트 7 〉 통과 (17.61ms, 10.1MB)
41 | 테스트 8 〉 통과 (47.09ms, 10.2MB)
42 | 테스트 9 〉 통과 (0.02ms, 10.2MB)
43 | 테스트 10 〉통과 (0.99ms, 10.3MB)
44 | 테스트 11 〉통과 (0.01ms, 10.2MB)
45 | 테스트 12 〉통과 (2.13ms, 10.3MB)
46 | 테스트 13 〉통과 (0.26ms, 10.2MB)
47 | 테스트 14 〉통과 (0.09ms, 10.2MB)
48 | 테스트 15 〉통과 (0.04ms, 10.2MB)
49 | 테스트 16 〉통과 (0.04ms, 10.2MB)
50 | 테스트 17 〉통과 (0.10ms, 10.2MB)
51 | 테스트 18 〉통과 (0.02ms, 10.2MB)
52 | 테스트 19 〉통과 (0.08ms, 10.3MB)
53 | """
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Softeer/README.md:
--------------------------------------------------------------------------------
1 | # Softeer - 문제풀이
2 |
--------------------------------------------------------------------------------
/Softeer/순서대로 방문하기/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 약 1시간 풀이 후 실패로 답지 참조
5 |
6 | 접근법
7 | - 주어진 변수의 범위가 굉장히 좁음 -> 구현에 집중
8 | - 특정 조건이 있는 그래프 탐색 -> 백트래킹
9 | - visit 배열을 초기화하는 작업이 필요
10 |
11 | 회고
12 | - 인덱스로 goals 좌표 접근하는 방식 익숙해지기
13 |
14 | """
15 |
16 | import sys
17 |
18 | lines = []
19 |
20 | for line in sys.stdin:
21 | lines.append(list(map(int, line.split())))
22 |
23 | n, m = lines[0] # 격자의 크기, 순서대로 방문해야 하는 칸의 수
24 | board = lines[1:n + 1] # 격자
25 | goals = [list(map(lambda val: val - 1, line)) for line in lines[n + 1:]] # 방문해야 할 칸의 위치 (x,y)
26 |
27 | visited = [[False] * n for _ in range(n)]
28 | directions = [(0,1), (1,0), (-1,0), (0,-1)]
29 | cnt = 0
30 |
31 | visited[goals[0][0]][goals[0][1]] = True
32 |
33 | # 이동이 유효한지 검사
34 | def is_valid(x, y):
35 | return (
36 | x > -1 and x < n and y > -1 and y < n and board[x][y] == 0 and not visited[x][y]
37 | )
38 |
39 | # 백트래킹 탐색
40 | def back_tracking(idx, x, y):
41 | # 마지막으로 방문해야 할 칸에 도달한 경우
42 | if idx == len(goals) - 1:
43 | if goals[idx] == [x, y]:
44 | global cnt
45 | cnt += 1
46 | return
47 |
48 | # 다음 칸을 목표로 백트래킹
49 | elif goals[idx] == [x, y]:
50 | back_tracking(idx + 1, x, y)
51 |
52 | # 상하좌우 이동 가능하면 방문, 백트래킹 탐색
53 | for dx, dy in directions:
54 | nx, ny = x + dx, y + dy
55 |
56 | if is_valid(nx, ny):
57 | visited[nx][ny] = True
58 | back_tracking(idx, nx, ny)
59 | visited[nx][ny] = False
60 |
61 | back_tracking(1, goals[0][0], goals[0][1])
62 |
63 | print(cnt)
--------------------------------------------------------------------------------
/Softeer/순서대로 방문하기/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 40분
3 |
4 |
5 | n: 격자의 크기(2 ≤ n ≤ 4)
6 | m: 순서대로 방문해야 하는 칸의 수(2 ≤ m ≤ n^2)
7 |
8 | n개의 수(0 or 1)
9 | - 0: 빈 칸
10 | - 1: 벽
11 |
12 | 방문해야할 m개의 칸 (x, y)가 주어진다
13 | - 주어진 칸에 벽이 있는 경우능 없고, 중복도 없다.
14 |
15 |
16 | n과 m이 매우 작기 때문에 DFS를 통해 모든 경우의 수를 확인한다.(브루트 포스)
17 | 이 때, 더 이상 다음 칸으로 이동할 수 없다면 탐색을 포기한다.(백트래킹)
18 |
19 | <시간복잡도>
20 | O(N^2)
21 | """
22 | import sys
23 | input = sys.stdin.readline
24 |
25 |
26 | def dfs(cur_x: int, cur_y: int, nxt_idx: int):
27 | """
28 | cur_x: 시작 칸의 x좌표
29 | cur_y: 시작 칸의 y좌표
30 | nxt_idx: 다음 칸의 인덱스
31 | """
32 | global cnt
33 | # 다음 칸에 도달한 경우
34 | if cur_x == go[nxt_idx][0] and cur_y == go[nxt_idx][1]:
35 | # 1) 마지막 칸까지 도달했다면 경우의 수를 1 증가시키고 종료
36 | if nxt_idx == m-1:
37 | cnt += 1
38 | return
39 | # 2) 이동한 칸이 마지막 칸이 아니라면 방문해야할 칸의 idx 증가
40 | else:
41 | nxt_idx += 1
42 |
43 | # 현재 위치 방문처리
44 | visited[cur_x][cur_y] = True
45 |
46 | for k in range(4):
47 | nx, ny = cur_x+dx[k], cur_y+dy[k]
48 | if (0 <= nx < n and 0 <= ny < n) and board[nx][ny] == 0 and not visited[nx][ny]:
49 | dfs(nx, ny, nxt_idx)
50 |
51 | visited[cur_x][cur_y] = False
52 |
53 |
54 | # n: 격자의 크기, m: 순서대로 방무해야 하는 칸의 수
55 | n, m = map(int, input().split())
56 | # 격자판(0: 빈 칸, 1: 벽)
57 | board = [list(map(int, input().split())) for _ in range(n)]
58 | # 방문처리 배열
59 | visited = [[False] * n for _ in range(n)]
60 |
61 | # 방문해야할 칸의 좌표
62 | go = []
63 | for _ in range(m):
64 | x, y = map(int, input().split())
65 | go.append((x-1, y-1))
66 |
67 | # 상, 하, 좌, 우 방향으로 이동
68 | dx = [1, -1, 0, 0]
69 | dy = [0, 0, 1, -1]
70 |
71 | # 경우의 수
72 | cnt = 0
73 | # 순차적으로 방문
74 | dfs(go[0][0], go[0][1], 1)
75 | print(cnt)
--------------------------------------------------------------------------------
/Softeer/자동차 테스트/dohyun.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 풀이시간
4 | - 10분
5 |
6 | 접근법
7 | - n 5만 이하 -> 대충 O(NlogN)
8 | - 3대의 자동차를 고르는것이 고정
9 | - 중앙값이 존재하지 않으면 0
10 | - 존재하면 경우의 수는 (중앙값보다 작은 값의 수) x (중앙값보다 큰 값의 수)
11 | - 즉 해당 값이 존재하는지 탐색하고, 존재한다면 인덱스를 잘 활용하면 됨 -> 이진탐색
12 |
13 | 회고
14 | - 3개의 값을 선택, 중앙값이라는 힌트로 이진탐색을 쉽게 눈치챌 수 있었음
15 |
16 | """
17 |
18 | import sys
19 |
20 | inputs = sys.stdin.readline
21 |
22 | n, q = map(int, inputs().split())
23 | yeonbi = [int(x) for x in inputs().split()]
24 | m_i = [int(inputs()) for _ in range(q)]
25 |
26 | yeonbi.sort()
27 |
28 | def binary_search(target, start, end):
29 | while start <= end:
30 | mid = (start + end)//2
31 | if yeonbi[mid] == target:
32 | return mid
33 | elif yeonbi[mid] > target:
34 | end = mid - 1
35 | else:
36 | start = mid + 1
37 | return -1
38 |
39 | for m in m_i:
40 | result = binary_search(m, 0, len(yeonbi)-1) # 이진탐색으로 인덱스 값 반환
41 |
42 | if result == -1 or result == 0 or result == len(yeonbi)-1: # 값이 없거나 작은수, 큰수가 없을 떄
43 | print(0)
44 | else:
45 | print((len(yeonbi)-result-1) * (result)) # 작은 수 * 큰 수
46 |
--------------------------------------------------------------------------------
/Softeer/자동차 테스트/jisu.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이 시작 : 2023-10-08 11:17
3 |
4 | #### 제한 사항
5 | - q <= 200,000 이므로, O(NlogN) 이하의 알고리즘을 설계해야 한다.
6 |
7 | #### 이전 풀이
8 | - 백트래킹으로 m보다 작은 수 하나, 큰 수 하나를 고르는 경우의 수를 구한다.(시간초과)
9 |
10 | #### 풀이
11 | - m을 확정한 상태에서 m보다 작은 하나, m보다 큰 하나를 고르는 경우의 수
12 | - 정렬 후 m의 위치 찾기(binary search)
13 | - [1, 2, 3, 5, 6] 에서 m이 3이면, 3은 fix, 나머지 경우의 수는 [1, 2]에서 하나, [5, 6]에서 하나
14 |
15 | 풀이 완료 : 2023-10-08 12:20 (소요 시간 : 1시간 3분)
16 | """
17 | import sys
18 | from typing import List
19 |
20 | input = sys.stdin.readline
21 |
22 |
23 | def binary_search(costs: List[int], target: int) -> int:
24 | """
25 | costs 내에서 target의 위치를 찾아 탐색 성공 시 위치를 반환한다.
26 | 탐색 실패 시 -1을 반환한다.
27 | """
28 | low, high = 0, n - 1
29 |
30 | while low <= high:
31 | mid = (low + high) // 2
32 | if costs[mid] == target:
33 | return mid
34 | elif costs[mid] < target:
35 | low = mid + 1
36 | else:
37 | high = mid - 1
38 |
39 | return -1
40 |
41 |
42 | n, q = map(int, input().rstrip().split())
43 | costs = sorted(list(map(int, input().rstrip().split())))
44 |
45 | for _ in range(q):
46 | idx = binary_search(costs, int(input().rstrip()))
47 |
48 | if idx == -1: # 탐색 실패 시
49 | print(0) # 0 반환
50 | continue
51 |
52 | print(idx * (n - idx - 1)) # line 13 참고
53 |
54 | """ 시간초과 (List.index() 사용)
55 | for _ in range(q):
56 | try:
57 | idx = costs.index(int(input().rstrip()))
58 | except:
59 | print(0)
60 | continue
61 |
62 | print(idx * (n-idx-1))
63 | """
64 |
--------------------------------------------------------------------------------
/Softeer/자동차 테스트/jisu_dfs.py:
--------------------------------------------------------------------------------
1 | """
2 | # 시간 초과 풀이
3 | 풀이 시작 : 2023-10-08 11:17
4 |
5 | #### 제한 사항
6 | - q <= 200,000 이므로, O(NlogN) 이하의 알고리즘을 설계해야 한다.
7 |
8 | #### 풀이
9 | - m을 확정한 상태에서 m보다 작은 하나, m보다 큰 하나를 고르는 경우의 수
10 | - 백트래킹으로 m보다 작은 수 하나, 큰 수 하나를 고르는 경우의 수를 구한다.(시간초과)
11 | """
12 | import sys
13 |
14 | sys.setrecursionlimit(10**6)
15 |
16 | input = sys.stdin.readline
17 |
18 | n, q = map(int, input().rstrip().split())
19 | costs = sorted(list(map(int, input().rstrip().split())))
20 | cnt = 0
21 |
22 |
23 | def dfs(idx: int, m: int, before: bool, after: bool):
24 | global cnt
25 |
26 | if before and after:
27 | cnt += 1
28 | return
29 |
30 | if idx == n:
31 | return
32 |
33 | if not before and costs[idx] < m: # 작은 값으로 하나 선택하는 경우
34 | dfs(idx + 1, m, True, after)
35 | elif not after and costs[idx] > m: # 큰 값으로 하나 선택하는 경우
36 | dfs(idx + 1, m, before, True)
37 |
38 | dfs(idx + 1, m, before, after) # 선택하지 않는 경우
39 |
40 |
41 | for _ in range(q):
42 | m = int(input().rstrip())
43 | cnt = 0
44 |
45 | if m in costs and m >= costs[1] and m <= costs[-2]:
46 | dfs(0, m, False, False)
47 | print(cnt)
48 |
--------------------------------------------------------------------------------
/Softeer/자동차 테스트/sumin.py:
--------------------------------------------------------------------------------
1 | """
2 | 풀이시간: 15분
3 |
4 |
5 | n: 자동차의 개수(1 ≤ n ≤ 50,000)
6 | q: 질의의 개수(1 ≤ q ≤ 200,000)
7 |
8 |
9 | 3대의 자동차에 대해서만 테스트가 가능하기 때문에
10 | [중앙값보다 작은 수들, 중앙값, 중앙값보다 큰 수들]라고 생각했을 때
11 | 중앙값보다 작은 수들 * 중앙값보다 큰 수들은 모든 경우의 수가 된다.
12 |
13 | <시간복잡도>
14 | O(NlogN + QlogN)
15 | """
16 | import sys
17 | from bisect import bisect_left
18 | input = sys.stdin.readline
19 |
20 | # 자동차의 개수, 질의의 개수
21 | n, q = map(int, input().split())
22 | # 자동차 연비
23 | efficiency = sorted(map(int, input().split()))
24 |
25 | for _ in range(q):
26 | # 중앙값
27 | m = int(input())
28 | idx = bisect_left(efficiency, m)
29 | # 중앙값이 자동차 연비에 존재하면 해당 수보다 작은 수들의 개수 * 큰 수의 개수
30 | if idx < n and efficiency[idx] == m:
31 | print(idx * (n - idx - 1))
32 | else:
33 | print(0)
--------------------------------------------------------------------------------
/Softeer/자동차 테스트/wooyeol.py:
--------------------------------------------------------------------------------
1 | """
2 | 자동차 테스트
3 | https://softeer.ai/practice/info.do?idx=1&eid=1717
4 |
5 | 풀이시간
6 | 14:41 ~ 15:02
7 | 21:36 ~ 21:46 (31분)
8 |
9 | 문제 조건
10 | * 1 ≤ n ≤ 50,000
11 | * 1 ≤ q ≤ 200,000
12 | * 1 ≤ 자동차의 연비 ≤ 1,000,000,000
13 | * 1 ≤ mi ≤ 1,000,000,000 (i는 1 이상 q 이하입니다. 즉, mi 는 각 질의에 대응하는 중앙값을 의미합니다.)
14 |
15 | 시간 복잡도 :
16 | O(nlogn + q * logn)
17 |
18 | 접근법
19 | 무슨 알고리즘으로 풀이 할 수 있을까? -> 이진탐색
20 |
21 | 1. 이진 탐색을 진행하기 위해서 정렬을 진행
22 | 2. 정렬된 리스트에서 입력받은 값이 몇번째 인덱스에 존재하는지 bisect_left를 통해 확인
23 | 3. 인덱스 값이 처음 혹은 마지막이라면 해당 값은 불가능
24 | 4. 아니라면 왼쪽의 개수와 오른쪽의 개수를 곱해서 경우의 수를 반환합니다.
25 | 5. 또한 입력받은 연비가 존재하지 않아도 0을 반환합니다.
26 | """
27 | import sys
28 | from bisect import bisect_left
29 |
30 | input = sys.stdin.readline
31 |
32 | # 데이터 입력
33 | n,q = map(int, input().split())
34 | cars = sorted(map(int, input().split()))
35 |
36 | # 연비 탐색을 위한 집합
37 | cars_set = set(cars)
38 |
39 | # Q번 입력받으며
40 | for _ in range(q):
41 | value = int(input())
42 |
43 | # 해당 연비가 존재한다면
44 | if value in cars_set:
45 | # 해당 인덱스 탐색 - 이진탐색(O(logN))
46 | value_idx = bisect_left(cars, value)
47 |
48 | # 처음과 마지막 인덱스가 아니라면 예외가 아님
49 | if not (value_idx == 0 or value_idx == n-1):
50 | left = value_idx
51 | right = n - 1 - value_idx
52 | # 경우의 수를 구하기 위해 왼쪽 개수와 오른쪽 개수를 곱한 값을 반환
53 | print(left * right)
54 | # 예외처리 : 처음과 마지막 인덱스일 경우 중앙값이 될 수 없음
55 | else:
56 | print(0)
57 | # 예외처리 : 주어진 연비가 존재하지 않음
58 | else:
59 | print(0)
60 |
--------------------------------------------------------------------------------
/✨ 효과 만점 코딩테스트 Cheat Sheet!/🚀 알고리즘 해킹 아이디어.md:
--------------------------------------------------------------------------------
1 | # 🚀 알고리즘 해킹 아이디어
2 |
3 | - [그래프 두 칸씩 가기](/%E2%9C%A8%20%ED%9A%A8%EA%B3%BC%20%EB%A7%8C%EC%A0%90%20%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8%20Cheat%20Sheet!/%F0%9F%93%9A%20%ED%8C%8C%EC%9D%B4%EC%8D%AC%20%EC%9E%90%EB%A3%8C%ED%98%95%20%ED%8A%B9%EC%A7%95%20%EB%AA%A8%EC%9D%8C.md#그래프-두-칸씩-가기)
4 |
5 |
6 |
7 | ### 그래프 두 칸씩 가기
8 |
9 | Note. 아래와 같이 두 대각선의 교점을 표현하기 어려울 때에는, 좌표평면을 두 배로 늘려버리자!
10 |
11 |
--------------------------------------------------------------------------------