diff --git a/README.md b/README.md index 99d3982..9ed64f9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ # 알고리즘 멋쟁이 1기 + 매주 2~3문제씩 알고리즘 문제를 풀고 서로 피드백 하며 성장하는 모임 + # 스터디 교재 + [이것이 취업을 위한 코딩 테스트다 with 파이썬](http://www.yes24.com/Product/Goods/91433923) + # 진행 방식 + - 매주 교재 한 단원씩 차례대로 각자 공부 - 해당 주차 단원에 맞는 백준 알고리즘 3문제 총무가 출제 - 총무는 매주 돌아가면서 바꿈 @@ -11,44 +16,52 @@ - 총무가 벌금 계산 후 -> 다음 주차 총무가 README.md 업데이트 (문제 출제) ## 0. 시작 + - git clone 후 각자 github 아이디명으로 branch 파기 + ## 1. 월 ~ 금요일 자정 + - 커리큘럼에 맞게 교재 공부 - 출제된 세 문제 풀기 - WIL(Weak I Learn).md 정리 및 코드 자신의 branch에 push - Weak I Learn: 그 주 자신이 배운 내용 정리한 .md 문서 - 내 브랜치 -> main 으로 pull request + ## 2. 토요일 + - 사이클에 맞춰 해당 사람 코드 PR 리뷰 - 주차별 해당 사람 = (자신의 번호 + 해당 주차) % 인원수 - 예) 1주차-seungwookim99 : (seungwookim99(0) + 1) % n = 1 => 1번 사람 코드 PR + ## 3. 일요일 + - 총무가 돈 계산 - 총무가 다음주 필수 문제 README.md 커리큘럼에 정리 - ## 맴버 및 번호 + ~~seungwookim99(0) -> siwonblue(1) -> keepgoing-hyeonsoo(2) -> itsnowkim(3) -> jonghyeonjo99(4)~~ seungwookim99(0) -> keepgoing-hyeonsoo(1) -> itsnowkim(2) -> jonghyeonjo99(3) - ## 벌금 규칙 + 1. 지각 : 금요일 자정 전까지 풀리퀘 날리기 - - 시간 당 500원 - - ex) 토요일 새벽 3시 : 3*500 = 1500원 벌금 -2. 결석 : 요일 자정 이후에 제출 혹은 미제출 + - 시간 당 500원 + - ex) 토요일 새벽 3시 : 3\*500 = 1500원 벌금 +2. 결석 : 요일 자정 이후에 제출 혹은 미제출 - 10000원 -3. 시험기간 : 합의하에 스터디 2주 휴식 +3. 시험기간 : 합의하에 스터디 2주 휴식 4. 탈퇴 시 : 취직했을 경우, 그 외에는 대타 구하고 나가기 - ## 코드 리뷰 예시 + 1. 코드의 시간 복잡도 2. 코드의 개선 방안 3. 추천해줄 새로운 함수나 라이브러리 -4. 그 외의 코멘트 +4. 그 외의 코멘트 ## 폴더 구조 + ``` README.md code @@ -71,6 +84,7 @@ code 총무 : seungwookim99(0) 필수 문제 + - https://www.acmicpc.net/problem/1541 - https://www.acmicpc.net/problem/1461 - https://www.acmicpc.net/problem/2138 @@ -82,20 +96,21 @@ code 총무 : siwonblue(1) 필수 문제 + - 2504 : 괄호의 값 https://www.acmicpc.net/problem/2504 - 14503 : 로봇 청소기 https://www.acmicpc.net/problem/14503 - 15686 : 치킨 배달 https://www.acmicpc.net/problem/15686 - ## **Week 3** -22.10.10 ~ 22.10.29 : 시험기간 휴회 +22.10.10 ~ 22.10.29 : 시험기간 휴회
22.10.30 ~ 22.11.05 : DFS/BFS 필수 문제 + - 2644 : 촌수계산 https://www.acmicpc.net/problem/2644 - 2583 : 영역구하기 https://www.acmicpc.net/problem/2583 @@ -109,6 +124,7 @@ code 22.11.06 ~ 22.11.12 : 정렬 필수 문제 + - 1931 : 회의실 배정 https://www.acmicpc.net/problem/1931 - 13334 : 철로 https://www.acmicpc.net/problem/13334 @@ -122,6 +138,7 @@ code 22.11.14 ~ 22.11.20 : 이진 탐색 필수문제 + - 16401 : 과자나눠주기 https://www.acmicpc.net/problem/16401 - 2470 : 두 용액 https://www.acmicpc.net/problem/2470 @@ -129,3 +146,17 @@ code - 1561 : 놀이공원 https://www.acmicpc.net/problem/1561 총무 : jonghyeonjo99(4) + +## **Week 6** + +22.11.21 ~ 22.11.27 : 다이나믹 프로그래밍 + +필수문제 + +- 11053 : 가장 긴 증가하는 부분 수열 https://www.acmicpc.net/problem/11053 + +- 1106 : 호텔 https://www.acmicpc.net/problem/1106 + +- 1513 : 경로 찾기 https://www.acmicpc.net/problem/1513 + +총무 : seungwooKim99(0) diff --git a/code/seungwookim99/week6/11053.py b/code/seungwookim99/week6/11053.py new file mode 100644 index 0000000..fe3fa20 --- /dev/null +++ b/code/seungwookim99/week6/11053.py @@ -0,0 +1,12 @@ +import sys + +N = int(sys.stdin.readline().rstrip()) +A = list(map(int, sys.stdin.readline().rstrip().split())) +DP = [0] * N +for i in range(N): + for j in range(i): + if A[j] < A[i]: + DP[i] = max(DP[i], DP[j]) + DP[i] += 1 + +print(max(DP)) \ No newline at end of file diff --git a/code/seungwookim99/week6/1106_1.py b/code/seungwookim99/week6/1106_1.py new file mode 100644 index 0000000..7fef59a --- /dev/null +++ b/code/seungwookim99/week6/1106_1.py @@ -0,0 +1,19 @@ +# 일부 구글링한 코드 +import sys + +C, N = map(int, sys.stdin.readline().rstrip().split()) +cities = [ + tuple(map(int, + sys.stdin.readline().rstrip().split())) for _ in range(N) +] +cities = sorted(cities, key=lambda x: (x[0], x[1])) +INF = int(1e9) +DP = [INF] * (C + 101) +DP[0] = 0 + +for i in range(N): + cost, benefit = cities[i] + for j in range(benefit, C + 101): + DP[j] = min(DP[j - benefit] + cost, DP[j]) + +print(min(DP[C:])) \ No newline at end of file diff --git a/code/seungwookim99/week6/1106_2.py b/code/seungwookim99/week6/1106_2.py new file mode 100644 index 0000000..3040573 --- /dev/null +++ b/code/seungwookim99/week6/1106_2.py @@ -0,0 +1,20 @@ +import sys + +C, N = map(int, sys.stdin.readline().rstrip().split()) +cities = [ + tuple(map(int, + sys.stdin.readline().rstrip().split())) for _ in range(N) +] +cities = sorted(cities, key=lambda x: (-(x[1] / x[0]), x[0])) + +INF = int(1e9) +DP = [INF] * (C + 101) +DP[0] = 0 + +for i in range(N): + cost, benefit = cities[i] + for j in range(benefit, C + 101): + if DP[j] > DP[j - benefit] + cost: + for k in range(benefit + 1): + DP[j - benefit + k] = min(DP[j - benefit] + cost, DP[j - benefit + k]) +print(DP[C]) \ No newline at end of file diff --git a/code/seungwookim99/week6/README.md b/code/seungwookim99/week6/README.md new file mode 100644 index 0000000..f7ac181 --- /dev/null +++ b/code/seungwookim99/week6/README.md @@ -0,0 +1,152 @@ +# Week 6 + +6주차에 대한 WIL + +# 11053 : 가장 긴 증가하는 부분 수열 + +## 😎 Solved Code + +### 💻 Code + +```python +import sys + +N = int(sys.stdin.readline().rstrip()) +A = list(map(int, sys.stdin.readline().rstrip().split())) +DP = [0] * N +for i in range(N): + for j in range(i): + if A[j] < A[i]: + DP[i] = max(DP[i], DP[j]) + DP[i] += 1 + +print(max(DP)) +``` + +### ❗️ 결과 + +성공 + +### 💡 접근 + +주어진 수열 A에 대해 DP 리스트를 만들어, A[0:i+1] 의 가장 긴 증가하는 부분 수열의 길이를 DP[i]에 담았다. + +따라서 DP를 0부터 n까지 bottom-up 방식으로 순회하며 구했다. 0 ~ i-1 까지 A의 원소를 살펴본 결과 i번째 원소보다 작을 경우 `max(DP[i], DP[j])` 로 DP[i]를 업데이트 했다. + +## 🥳 문제 회고 + +간단한 DP 문제였던 것 같다. + +# 1106 : 호텔 + +## 😎 Solved Code + +### 💻 Code1 + +```python +import sys + +C, N = map(int, sys.stdin.readline().rstrip().split()) +cities = [ + tuple(map(int, + sys.stdin.readline().rstrip().split())) for _ in range(N) +] +cities = sorted(cities, key=lambda x: (x[0], x[1])) +INF = int(1e9) +DP = [INF] * (C + 101) +DP[0] = 0 + +for i in range(N): + cost, benefit = cities[i] + for j in range(benefit, C + 101): + DP[j] = min(DP[j - benefit] + cost, DP[j]) + +print(min(DP[C:])) +``` + +### 💻 Code2 + +```python +import sys + +C, N = map(int, sys.stdin.readline().rstrip().split()) +cities = [ + tuple(map(int, + sys.stdin.readline().rstrip().split())) for _ in range(N) +] +cities = sorted(cities, key=lambda x: (-(x[1] / x[0]), x[0])) + +INF = int(1e9) +DP = [INF] * (C + 101) +DP[0] = 0 + +for i in range(N): + cost, benefit = cities[i] + for j in range(benefit, C + 101): + if DP[j] > DP[j - benefit] + cost: + for k in range(benefit + 1): + DP[j - benefit + k] = min(DP[j - benefit] + cost, DP[j - benefit + k]) +print(DP[C]) +``` + +### ❗️ 결과 + +성공 - code1은 일부 구글링 + +### 💡 접근 + +### Code 1 + +DP를 이용해 memoization 기법으로 cost list를 중간 저장하고, 이를 불러와서 cost list(위 코드에서 DP 리스트)를 업데이트 하는 방식으로 해결해야겠다고 생각했다. 근데 막상 떠올리려 하니 상당히 어려웠다. + +그럼에도 한가지 규칙을 정하고 문제에 접근해보고자 했다. + +- 어떠한 기준으로 `도시를 홍보할 때 드는 비용, 얻을 수 있는 고객`을 정렬한다. 이는 cities에 튜플리스트로 저장했다. +- 도시를 순회하며 DP list 전체를 업데이트 해 나간다. + +정렬은 `비용에 대한 오름차순` 으로 했다. 아무래도 비용이 적은 것 부터 먼저 채워나가는 것이 그리디 알고리즘 측면에서 정답에 빠르게 다가갈 수 있다고 생각했다. (나중에 알게 된 것인데 어차피 결국 모든 도시를 순회하며 DP list를 최적의 상태로 업데이트 하기 때문에, 도시를 정렬하는것은 의미가 없어보이기도 하다… 확실하지는 않지만 정렬 기준을 바꿔도 정답이 나왔다) + +그럼 cities에 대해 하나씩 순회를 하며 DP 리스트를 업데이트 한다. + +`DP[j] = min(DP[j - benefit] + cost, DP[j])` 을 보면 기존의 DP[j]와 DP[j - benefit] + cost 를 비교해 최솟값으로 업데이트 한다. benefit은 얻을 수 있는 고객 수 인데, j - benefit 명의 최소 비용에 cost를 더하면 j명에 대한 새로운 비용이 나오고, 이는 기존의 DP[j]보다 작을 가능성이 있기 떄문이다. 이런 방식으로 모든 city에 대해 업데이트를 해준다. + +최종 답은 `min(DP[C:])` 으로 구했다. 최솟값이 C 이상에서 나올 수 있기 떄문이다. + +최종 답을 도출하는 과정을 구글링 했는데,, 사실 와닿지가 않았다. 문제를 풀며 비슷하게 접근했지만, 매 iteration마다 항상 최적의 cost를 담고 있지 않기 때문에 마지막에 min(DP[C:]) 로 문제를 해결한 것 같았다. 사실 `C명 이상의 cost중에서 반드시 정답이 나오는지` 납득이 안됐다. 반례가 있지 않을까 생각도 들었고 이해도 잘 안되고 찝찝해서 내 식대로 다시 풀어봤다. + +### Code 2 + +code 1 과 비슷하게 접근했지만, cities를 순회하는 iteration이 끝날 때 마다 항상 최소의 비용을 저장하고 있게 했다. 바꾼 부분은 아래와 같다. + +```python + for j in range(benefit, C + 101): + if DP[j] > DP[j - benefit] + cost: + for k in range(benefit + 1): + DP[j - benefit + k] = min(DP[j - benefit] + cost, DP[j - benefit + k]) +``` + +만약 DP[j] > DP[j - benefit] + cost 라면 code 1 에서는 `DP[j] = min(DP[j - benefit] + cost, DP[j])` 으로 DP[j]만 업데이트 했다. 하지만 실제로는 DP[j - benefit] 부터 DP[j] 까지 비용이 최적화 될 수 있다. 예를 들어 아래와 같은 상황이다. + +```python +# 현재 DP +DP = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 ...] + +# 다음 iteration +cost, benefit = 3, 5 + +# DP[j - benefit] = DP[5 - 5] = 0 +# DP[j] = DP[5] = 5 +즉, DP[j] == 5 > DP[j - benefit] + cost == 3 +DP[0], DP[1], DP[2], ... DP[5] 중 DP[4], DP[5]가 cost == 3 으로 최적화 될 수 있다. + +# for k in range(benefit + 1) 처리 이후 +DP = [0,1,2,3,3,3,4,...] +``` + +위와 같이 매 iteration 마다 항상 최적의 cost를 저장하면 마지막에 정답 출력시에 DP[C]를 출력하면 된다. + +시간복잡도는 O(N*C*benefit의 최댓값) 이고 N*C*benefit 최댓값 = 20 _ 1000 _ 100 = 200만 으로 제한된 시간 안에 해결이 가능하다. + +## 🥳 문제 회고 + +DP가 정말 어려운 것 같다… 특히나 비중이 높고 중요한 주제인 만큼 많은 연습이 필요해 보인다.