-
Notifications
You must be signed in to change notification settings - Fork 0
4주차 문제풀이(2) #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mututu17
wants to merge
18
commits into
main
Choose a base branch
from
이분탐색
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
4주차 문제풀이(2) #11
The head ref may contain hidden characters: "\uC774\uBD84\uD0D0\uC0C9"
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
시간 내서 고민하다 보니까 풀리긴 했다. 골드 3이라 확실히 문제를 읽자마자 막막했는데 이 때까지 풀었던 개념을 응용하니까 어찌어찌 풀었다. 블로그나 다른 사람 백준 코드를 좀 찾아봐야겠다.
이제 트리 문제가 나오네요 트리를 연결리스트로 구현해야 될 줄알았는데 이진 트리는 pair로 간단하게 구현이 된다는 걸 블로그에서 봐서 수월했습니다. inorder와 postorder를 어찌어찌 만들긴 했는데 블로그를 보니까 더 간단하게 만들길래 수정해볼 생각입니다.
root가 '.'일 때만 넘어가고 출력 왼쪽 오른쪽 왼쪽 출력 오른쪽 왼쪽 오른쪽 출력 이 구조로 만들면 간단한데 이걸 생각못했네
메모리 초과로 실패했습니다. 처음에 트리를 저장할 때 2차원 벡터로 받았던게 문제인거 같다.
2차원 벡터가 문제였나 해서 바꿀려고 봤더니 무조건 써야할 거 같아서 그럼 어떡하지 연결리스트로 풀어야하나 하다가 블로그를 찾아봤는데 제가 푼거랑 똑같은 알고리즘으로 벡터만 배열로 바꿔서 풀었더라구요 벡터가 메모리를 많이 잡아먹는다는 걸 배웠고 이제 배열을 적극 활용할 생각입니다..
일단 구현은 했는데 솔직히 시간 초과 날 거 같았다. 해결 방법은 생각해봐도 모르겠어서 블로그 찾아봤다.
어떤 노드에서든 그 노드에서 가장 거리가 먼 노드를 구하면 (leaf 노드) leaf 노드에서 DFS를 했을 때 트리의 지름을 구할 수 있다는 수학적인 결론을 내려야 모든 트리를 검사하지 않고 풀 수있다. 이런 머리가 잘 돌아가야 골드2를 푸나보다. leaf노드를 구하기 위해선 반환형을 int로 해서 거리를 출력하는 방식이 아니라 void형에 count를 매개변수로 받아서 구하는 방식으로 풀어야했다. 두 방식의 차이점을 잘 알 수 있었다.
마찬 가지로 트리의 지름을 활용해서 푸는 문제 1167을 조금만 변형해서 쉽게 풀었다.
문제가 많아서 담아 놨어요
이분 탐색 생각보다 쉬웠음
이분 탐색 첫 문제라서 개념 정리할 겸 블로그 보고 공부했습니다.
코드 짜는 건 쉬운데 아이디어 생각하는게 개어렵네요
이게 왜 시간초과지
억까 당했다... 다른 사람 코드 볼 때 마다 이 코드는 왜 적는 걸까 궁금했는데 사실 빠른 입출력을 위한 코드였고 C++은 이거 없으면 틀리는구나
원시적인 방법으로 풀어서 시간초과 날 거 같았다. 근데 해결할 방법이 생각이 안난다.
해쉬를 쓸 생각은 했었다. 하지만 카드가 음수로도 나오고 범위가 천만 이라서 배열로 해쉬를 쓰면 문제가 생긴다고 판단해서 이분탐색으로 고민하다가 블로그를 찾아봤다. map이라는 자료구조를 쓰면 해결이 된다는게 너무 신기하고 이분탐색으로도 upper_bound 이런 함수를 써서 풀 수 있다고 한다. 이런 문제는 블로그를 무조건 찾아봐야하는 문제 인거 같다. 내가 모르는 세상을 이런 문제를 통해 알 수 있으니까
Collaborator
|
저도 군대에서 랜선 많이 만들었는데.. 그립네요,, 가 아니라 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
이번엔 이분 탐색 문제를 풀었습니다!
이분 탐색을 어디에 적용해야 할 지 정할 때
이걸 이렇게 쓴다고?? 하면서 창의적인 생각에 놀랐습니다.
문제를 몇 개 풀다 보니까 각이 점점 보이기 시작하는데 여전히 어려운 문제를 만나면 막히네요
(삼분 탐색 문제는 보다가 벽 느끼고 패스했습니다.)
### 1. 랜선 자르기 - 1654
가지고 있는 K개의 랜선을 잘라서 N개의 랜선을 만들어야 하는 문제입니다.
예시 입력:
4 11
802
743
457
539
출력:
200
상세한 조건은 문제를 직접 읽어보시는 걸 추천합니다..
문제에서 요구하는 바는
802 랜선에서 200짜리 랜선 4개를,
743 랜선에서 200짜리 랜선 3개를,
457 랜선에서 200짜리 랜선 2개를,
539 랜선에서 200짜리 랜선 2개를,
만들어서 총 11개의 랜선을 만드는 겁니다.
11개의 랜선을 만들기 위해 랜선을 만들었을 때 랜선의 최대 길이를 구해야합니다.
여기서 랜선의 길이가 만약 201이 된다면
802 랜선에서 4개를 만들 수 없기 때문에 최대 길이는 200이 되는 것입니다!
제가 이 문제를 보고 처음 든 생각은

랜선의 길이를 모두 더한 값에서
802 + 743 + 437 + 539 = 2541
만들어야하는 랜선의 개수 11을 나누면
2541 / 11 = 231이 나오는데
231에서 1씩 빼면서 각 랜선에서
실제로 11개가 만들어지는 지 확인해가면
ex)
802 에서 3개
743 에서 3개
11개를 실제로 만들 수 있을 때
그 값이 최대 길이가 된다.
이 방식으로 코드를 짜려 했습니다.
하지만 문제의 의도는 당연히 이게 아닐 것이고
N이 백만이 된다고 생각하면
1씩 빼면서 계속 확인하는건 시간초과가 안나면 이상한 코드라고 생각했습니다.
이분 탐색 알고리즘은 정렬되어있는 데이터에서
범위를 절반씩 줄여가면서 값을 찾는 알고리즘으로 시간복잡도는 O(logN)으로
단순히 시작부터 끝을 다 탐색하는 선형탐색( O(N) )보다 빠르다.
이분 탐색을 사용하기위해서
답이 될 수 있는 랜선의 최대길이의 범위를 정해야한다.
1 ~ 주어진 랜선 중 가장 큰 랜선
1은 너무 작아서 당연히 11개 이상 만들 수 있을 것이고
가장 큰 랜선의 길이(803)은 만들 수 있는 랜선의 최대 길이라서 11개를 만들기 어려울 것이다.
(1개 밖에 못만든다.)
1 ~ 803 사이의 값 중 답이 될 수 있는 최대값을 탐색하여 문제를 푸는 것이 핵심이다!
left를 1로 right를 MAX_LAN(803)으로 설정하여
그 중간 값 mid를 구해서
mid = (right + left) / 2;
랜선 11개를 만들 수 있는 지 확인한다.
11개의 랜선을 만들 수 있다면 지금 mid값 보다 더 큰 값에서도
11개의 랜선을 만들 수 있는 지 확인해서 mid값을 더 큰 값으로 갱신해준다.
이렇게 left를 mid 다음 값으로 옮겨서 탐색을 이어나간다.
left = mid + 1;
result = max(result, mid);
11개의 랜선을 만들 수 없다면 지금 mid값 보다 더 작은 값에서는
11개의 랜선을 만들 수 있는 지 확인해본다.
right = mid - 1;
이 알고리즘으로 while문을 돌아서 left <= right가 깨지게 되면
그 때 result값이 답으로 나오게 된다.
이분 탐색 문제는 다 풀고나니까 코드도 39줄 밖에 안나오고
이분 탐색을 어떻게 적용할지만 잘 생각해보면 어렵지 않았다.
한 번 풀어보는거 강력 추천!
한가지 까다로웠던건
랜선의 길이가 2^31-1의 자연수로 주어진다고 했는데
이런 랜선이 100만개가 있을 수 있기 때문에
생각없이 int형으로 받아서 풀면 틀린다.
long long형으로 받아야 한다....
### 2. 나무 자르기 - 2805
마찬가지로 이분 탐색 문제이다.
상세한 설정은 직접 문제를 읽어보는 걸 추천드립니다...
문제에선 N그루의 나무가 주어지고
절단기의 높이를 설정하여 나무를 벨 수 있다.
나무를 베어서 M미터의 나무를 얻어야 하는데,
최소한의 나무를 베어서 적어도 M미터의 나무를 얻어야 한다.
예시 입력:
4 7
20 15 10 17
출력:
15
절단기의 높이를 15로 설정하면
20m 나무에서 5m
15m 나무에서 0m
10m 나무에서 0m
17m 나무에서 2m
를 베어서 딱 7m의 나무를 얻을 수 있다.
이분 탐색을 해야하는 값은 절단기의 높이이고
범위는 0 ~ 20m(주어진 나무 중 가장 큰 나무의 길이)이다,
0은 주어진 나무를 다 베어리겠다는 의미이고,
20은 안베겠다는 말이다.
M이 1 보다 큰 값이 주어지기 때문에

저는 max_tree - 1을 최대 범위로 설정했습니다.
(가장 큰 나무에서 1m만 베겠다는 뜻)
방금 전 풀었던 문제와 마찬가지로 left와 right값을 설정해서
그 절반인 mid값이 조건을 만족하는 지 확인합니다. (M미터의 나무를 얻을 수 있는가?)
조건을 만족한다면 절단기의 높이를 더 높여도 M미터의 나무를 얻을 수 있는 지 확인하여
절단기의 높이를 계속 갱신해주고
만족하지 않는다면 절단기의 높이를 낮추면 M미터의 나무를 얻을 수 있는 지 확인합니다.
그리고 left <= right가 깨지면 그 때 result값이 답입니다.
이 문제도 count를 long long 형으로 설정하는 거만 조심해서 쉽게 풀었습니다.
### 3. 공유기 설치 - 2110
집의 개수(N)와 설치해야 하는 공유기의 개수(C)가 주어진다.
다음 줄에 집의 좌표가 N개 주어진다.
예시 입력:
5 3
1
2
8
4
9
이 친구는 와이파이를 쓸 수 있는 구역을 최대한 늘리고 싶기 때문에 공유기를 최대한 떨어뜨려 놓고 싶어 한다.
따라서 가장 인접한 공유기의 거리를 최대로 하고 싶어한다.
출력:
3
여기서 3은 가장 인접한 공유기의 거리가 3이란 뜻이고
1 4 8
1 4 9에 공유기를 설치하면 1 4 사이의 거리가 3이므로
공유기 사이의 거리는 적어도 3 이상 되는 것이다!
이 문제의 의도를 이해하는게 너무 어려워서 시간도 엄청 쓰고 결국 블로그를 찾아봤다.
https://velog.io/@ngchaneok/%EB%B0%B1%EC%A4%80-2110%EB%B2%88-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EC%84%A4%EC%B9%98-C
여기서 알려준 이 문제의 핵심은
탐색하는 값의 범위는
최소거리(1) ~ 최대거리 (마지막 집 - 첫 번째 집)
mid값을 중간값으로 잡고
일단 첫 번째 집에 공유기를 설치한다.
생각해보면 설치 안 할 이유가 없다. 공유기 사이의 거리를 최대로 하고 싶은데
두 번째 집에 공유기를 설치하는 것보단 첫 번째 집에 설치하는 게 다음 공유기와 간격이 더 커지기 때문이다.
for문으로 0 ~ N-1까지 돌면서
마지막으로 공유기를 설치한 좌표부터 떨어진 거리가
mid 보다 크거나 같아지면 공유기를 설치한다.
이러면 설정한 최대 거리보다 더 인접한 곳에 공유기가 설치될 일이 없다.
공유기를 설치할 때 마다 count를 증가시키면
for문을 다 돌았을 때 공유기를 몇 개 설치 했는 지를 확인해본다.
공유기를 C개 이상 설치했다면 더 최적의 값을 찾기위해 left 값을 키우고
설치하지 못했다면 조건을 만족하기 위해 right값을 줄인다.
이 방식으로 문제를 해결할 수 있다.
설명하다보니 생각난건데 이렇게 출력된 mid값이 항상 C개의 공유기를 설치했다고 할 수 있을까..?
최적의 값을 찾기 위해 계속 탐색하기 때문에 최적의 값에선 C개를 설치했을 것 같지만
데이터가 이상하게 들어오면 C개보다 많이 설치한 경우도 나올 수 있으려나..?
일단 맞았으니 넘어갈 생각이다..
### 4. 숫자 카드 - 10815
이 문제는
상근이가 가지고 있는 카드의 개수
상근이가 가지고 있는 카드 리스트
확인 해야 하는 카드의 개수
확인 해야 하는 카드 리스트
예시 입력:
5
6 3 2 10 -10
8
10 9 -5 2 3 4 5 -10
가 이렇게 주어지고,
출력:
1 0 0 1 1 0 0 1
가지고 있다면 1을
없다면 0을 출력하면 되는 문제이다.
이분 탐색을 활용하는 정석적인 문제?
정렬된 리스트속에서 찾고 있는 값이 mid보다 크다면 mid오른쪽에서 찾고
작다면 mid 왼쪽에서 찾는다.
mid와 같다면 찾은 것이다.
찾았다면 1로 바꿔준다.
이 방식으로 확인해야하는 카드를 입력받을 때 마다
카드가 있는 지 없는 지 0 또는 1로 출력해준다.
이렇게 간단하게 풀리는 문제인 줄 알았으나

시간 초과를 당했다. 왜지???
다른 사람 코드를 볼 때 마다 이 부분은 왜 있는거지? 궁금했는데
이제 알게됐다. cin과 cout을 사용할 때 더 빠르게 해주는 코드인 거 같다?
이걸 추가해주니까 시간 초과가 안뜨고 맞았다.
C++정도면 빠른 언어라고 생각해서 이런 억까를 안당할 줄 알았는데
cout 보다 printf가 빠르다는 걸 배웠긴 했는데
입출력으로 억까당할줄은 몰랐다 ㅋㅋㅋㅋㅋ
앞으로 이 코드를 적으면 입출력 시간도 줄일 수 있고 억까 당할 확률도 막아준다.
좋은 거 하나 배웠다...
### 5. 숫자 카드2 - 10816
앞에 문제에서 조금만 바꼈다.
예시 입력:
10
6 3 2 10 10 10 -10 -10 7 3
8
10 9 -5 2 3 4 5 -10
이제 중복된 카드도 입력으로 들어오고
확인해서 출력할 때
출력:
3 0 0 1 2 0 0 2
이 카드가 몇 개 있는 지까지 출력해야 한다.
이렇게 되면 문제점이 아까 짰던 코드대로 이분 탐색을 하면 "카드가 있다 없다" 만 알 수 있고

몇 개 있는 지는 알 수 없는데
원시적인 방법으로 해결해봤다.
찾았을 때 오른쪽으로 1씩 증가해 나가면서 카드가 더 있는 지 확인하고
왼쪽으로도 카드가 더 있는 지 확인해서 출력하면
카드가 몇 개 있는 지 출력할 수 있게 된다.
결과는??
당연히 시간초과다.
같은 카드가 많을 경우 반복횟수가 많아져서 시간 초과가 나는 것 같다.
아까 문제도 입출력까지 줄여가면서 간발의 차로 통과한 거 보면 이분 탐색 한 번으로 몇 개 있는지까지 나와야 하는 거 같다?
어떻게 풀어야 하는지 감이 안잡혀서 블로그를 찾아봤다.
https://bbeomgeun.tistory.com/18
해쉬를 쓸 생각은 했었다.
하지만 카드가 음수로도 나오고 범위가 천만 이라서
배열로 해쉬를 쓰면 문제가 생긴다고 판단했었다.
하지만 map이라는 자료구조를 쓰면 해결이 된다.
map은 key를 입력하면 value를 출력하는 구조의 자료구조인데
배열은 0 ~ 자연수 까지의 인덱스에 저장된 값을 출력해주는 반면
map은 int, int로 map을 선언하면 (map<int, int> map;)
int값을 넣었을 때 그 값에 상응하는 int값을 출력해준다.
배열은 0 ~ 천만을 선언하면 메모리가 부족해지는 경우가 생기는데

map은 필요한 정보(입력 받은 카드)만 저장하기 때문에 메모리가 부족할 일은 아마 없을 것이다.
코드가 단순하다.
card를 입력받으면 map에 해당 번호의 값을 1 더해준다.
여러 번 카드를 입력받아도 1을 계속 더해줘서 카드가 몇 개 있는 지 저장할 수 있는 것이다.
card를 입력하면 map[card]에 저장된 값을 출력해주면 된다.
이렇게 풀면 이분탐색을 안쓰고 풀 수 있다.
옛날에도 이런 느낌의 문제를 푼 적이 있는데
이런 식으로 푸는 걸 "해시(Hash)"를 사용해서 푼다고 하더라
https://kbw1101.tistory.com/55
각각의 데이터가 유일한 key값을 가지게 해서
key를 입력받았을 때 빠르게 value를 출력해주는 자료구조?
예전에 푼 문제는 배열로 해쉬를 쓰는 걸 알려줬었는데
이제 map을 사용하면 범위가 큰 정수가 입력으로 들어오거나 string으로 입력이 들어와도
매칭된 값을 출력해줄 수 있다는 걸 배웠다.
아까 블로그에선 이분 탐색으로 upper_bound 이런 함수를 써서 풀 수도 있다고 알려준다. 나중에 공부해 보겠다...
이런 문제는 블로그를 무조건 찾아봐야 하는 문제인 거 같다. 내가 모르는 세상을 이런 문제를 통해 알 수 있으니까
### 6. 민호와 강호 - 11662

삼분탐색 문제라고 해서 도전해봤는데
어려워서 블로그를 찾아봤다.
https://m.blog.naver.com/zbqmgldjfh/222444891644
3차 함수가 나오고 lo 지점에서 p지점까지는 확인해볼 필요가 없어지는 것이다.
이렇게 잘 설명은 해주셨는데 뭔 소린지 솔직히 모르겠어서 포기했다.
거리를 시간에 따른 함수로 바꿔서 언제가 가장 가까운 지를 구해야 할 때
삼분 탐색을 쓰는 것 같았다.
가끔씩 이런 마이너한 문제를 추천해주는데
개념을 어떻게 응용하는 지 알려줘서 좋긴 하지만 아직 내가 풀 수준이 아닌 것 같아서
일단은 패스하려고 한다.
다음은 분할 정복을 풀어볼 생각입니다!
풀고 나서 풀리퀘를 적으면서 핵심을 정리하니까
복습도 되고 공부가 잘되네요
매 번 읽어주셔서 감사합니다! 파이팅!