Skip to content
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

Vinjunho/feature/boj 2075 N번째큰수 #298

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Vinjunho/boj/2075/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 백준 2075 N번째 큰 수

[문제 링크](https://www.acmicpc.net/problem/7662)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제 링크가 잘못되었습니다!!!!! 수정 부탁드립니다


## 1. 설계 로직

1. n^2개의 수 중에서 n번째의 수를 구하는 문제이다.
2. n^2개의 데이터를 모두 관리할 필요가 없다
3. 상위 n개만 관리하여 n번째 수를 뽑아내면 되겠다.
4. 이분탐색을 통해 랭킹을 관리하면 더 효율적.



- (n^2)log(n) , 1<=n<=1500

## 2. 코드

```python
import sys, bisect

input = sys.stdin.readline
n = int(input())
lank = [] # 상위 n개의 수만 관리할 list
for _ in range(n):
li = list(map(int, input().split())) # readline
for i in range(n):
bisect.insort(lank, -li[i]) # 크기순으로 정렬하며 삽입한다(bisect는 이분탐색활용하므로 O(log(n))
if len(lank) > n: # 상위 n번째 이 후의 수는 볼 이유 없으므로 삭제
lank.pop() # 리스트 맨 끝을 날리는 pop()함수는 O(1)
print(-lank[-1]) # n번째 수를 출력

```



## 3. 후기

- 어제 우선순위큐와 이분탐색에 대해 공부하다 bisect라는 말도 안 되는 라이브러리를 알게되었다.
- 앞으로 이분탐색이 필요할 때 많이 써먹어야겠다.
- 파이썬 사기

12 changes: 12 additions & 0 deletions Vinjunho/boj/2075/boj2075.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import sys, bisect

input = sys.stdin.readline
n = int(input())
lank = [] # 상위 n개의 수만 관리할 list
for _ in range(n):
li = list(map(int, input().split())) # readline
for i in range(n):
bisect.insort(lank, -li[i]) # 크기순으로 정렬하며 삽입한다(bisect는 이분탐색활용하므로 O(log(n))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bisect 미쳤네요

if len(lank) > n: # 상위 n번째 이 후의 수는 볼 이유 없으므로 삭제
lank.pop() # 리스트 맨 끝을 날리는 pop()함수는 O(1)
print(-lank[-1]) # n번째 수를 출력
144 changes: 144 additions & 0 deletions Vinjunho/boj/7662/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# 백준 7662 이중 우선순위 큐

[문제 링크](https://www.acmicpc.net/problem/7662)

## 1. 설계 로직

- ### java

1. 입력과 출력에 log(n) 을 원함
2. 정렬된 자료구조가 필요함
3. 중복을 제거해 줄 자료구조가 필요함
4. Treemap으로 한 번에 해결
5. I면 Treemap에 넣고 이미 존재하면 개수만 늘린다
6. D 1 이면 lastkey를 활용해 맨 오른쪽 노드 제거, D -1이면 firstkey를 활용해 맨 왼쪽 노드 제거(개수가 남아 있다면 개수만 1 줄임)
7. 비어있다면 Entry, 존재하면 lastkey, firstkey를 각각 출력한다

- n log(n)



- ### python

1. 입력과 출력에 log(n) 을 원함
2. 정렬된 자료구조가 필요함
3. 중복을 제거해 줄 자료구조가 필요함
4. 작은 수를 기준으로 하는 우선순위 큐, 큰 수를 기준으로 하는 우선순위 큐 이렇게 2개 만든다
5. I이면 두 큐에 모두 저장한다. 단, 인덱스 값을 넣어서 서로가 같은 수임을 인식시킨다
6. D 1 이면 내림차순 큐에서 제거, D -1이면 오름차순 큐에서 제거
7. 하나의 명령이 끝날 때마다 동시에 pop 값이 없는지 양쪽 큐를 확인한다. 있다면 최대한 pop한다.
8. 비어있다면 Entry, 존재하면 각 큐를 pop하여 출력한다

- n log(n)

## 2. 코드

- ### java

```python
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
for (int tc = 0; tc < T; tc++) {
int n = Integer.parseInt(br.readLine());
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
String command = st.nextToken();
int num = Integer.parseInt(st.nextToken());
if (command.equals("I")) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
} else {
if (map.size() > 0) {
int del_key = 0;
if (num == 1) {
del_key = map.lastKey();
} else {
del_key = map.firstKey();
}
int del_num = map.get(del_key) - 1;
if (del_num == 0) {
map.remove(del_key);
} else {
map.put(del_key, del_num);
}
}
}
}
if (map.size() == 0) {
System.out.println("EMPTY");
} else {
System.out.println(map.lastKey() + " " + map.firstKey());
}
}

}
}
```

- ### python

```
import sys
import heapq

input = sys.stdin.readline
T = int(input())
for _ in range(T):
n = int(input())
nq, mq = [], []
visit = [False] * (n + 1) # 지워진 번호 (입력된 순서)
for i in range(n):
command, num = input().split()
num = int(num)
if command == 'I': # 입력 명령이면
heapq.heappush(nq, (num, i)) # 작은수우선
heapq.heappush(mq, (-num, i)) # 큰수우선
continue
elif num == 1 and mq: # 지우라는 명령어에 지울 값이 있으면
visit[heapq.heappop(mq)[1]] = True # 입력된 값을 지우고 지웠음을 저장한다
elif num == -1 and nq: # 지우라는 명령어에 지울 값이 없으면
visit[heapq.heappop(nq)[1]] = True # 입력된 값을 지우고 지웠음을 저장한다
while nq and visit[nq[0][1]]: # nq에서 제거할 수 있을만큼 제거하겠다.
heapq.heappop(nq)
while mq and visit[mq[0][1]]: # mq에서 제거할 수 있을만큼 제거하겠다.
heapq.heappop(mq)
print(f'{-mq[0][0]} {nq[0][0]}' if nq else 'EMPTY')

```

## 3. 후기

- ### java

- 이 문제는 AVL Tree 혹은 Red-Black Tree를 활용하라는 문제로 보인다.

- 그러나 이 문제에서 자료구조 그 자체를 구현하기에는 무리가 있으므로 있는 자료구조를 활용해서 최대한 문제에 접근하는 것이 중요하겠다.

- Java에는 Treemap이라는 파워풀한 자료구조(RB Tree 기반)가 존재해서 쉽게 풀 수 있었다.

- Treemap은 Map의 Key, Value값을 가지면서 이분탐색트리의 구조를 띄고 있어 정렬된 구조를 갖는다.

- 입력,출력에 log(n) 밖에 걸리지않고, 내부적으로 균형을 맞추기위해 회전도 알아서 해준다.

- 자바 사기

- ### python

- Python에는 정렬이 되며 이분탐색하는 자료구조는 존재하지 않는다. 있다고 하더라도 삭제에 log(n) 이 소모되는 자료구조는 기본적으로 제공하지 않는듯하다.
- 우리가 알아야 할 값은 최소값, 최대값으로 기준이 2개이므로 각각의 기준에 맞는 우선순위큐를 활용한다.
- 서로의 큐가 같은 데이터를 공유하고 있어야 한다는 점에서 인덱스를 활용하는 방법과 dictionary를 활용하는 2가지의 방법이 있겠다
- 삽입에 이분탐색을 적용하고 삭제에 O(n)을 소비시켜도 통과하는 것도 확인했다. 저격케이스 뜨면 바로 시간초과다. 그러나 의도가 중복을 제거하라는 부분이라 삭제 시간 초과는 큰 문제가 아닌 것 같다. 심지어는 속도도 더 빠른 결과가 나와 오히려 당황스럽다. 하하
- 자바 사기

49 changes: 49 additions & 0 deletions Vinjunho/boj/7662/boj7662.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class boj7662 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
for (int tc = 0; tc < T; tc++) {
int n = Integer.parseInt(br.readLine());
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
// TreeMap은 정렬과 동시에 Map의 역할을 수행 key는 입력받은 숫자, value는 개수
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
String command = st.nextToken();
int num = Integer.parseInt(st.nextToken());
if (command.equals("I")) {
if (map.containsKey(num)) { // 이미 존재하는 값이면
map.put(num, map.get(num) + 1); // 개수만 증가
} else {
map.put(num, 1); // 없으면 추가
}
} else {
if (map.size() > 0) {
int del_key = 0; // 지울 key 초기화
if (num == 1) {
del_key = map.lastKey(); // 가장 뒤에 있는 = 가장 키값이 큰
} else {
del_key = map.firstKey(); // 가장 앞에 있는 = 가장 키값이 작은
}
int del_num = map.get(del_key) - 1; // 개수를 줄여보고
if (del_num == 0) { // 0이면 삭제
map.remove(del_key);
} else {
map.put(del_key, del_num); // 그 외엔 업데이트
}
}
}
}
if (map.size() == 0) {
System.out.println("EMPTY");
} else {
System.out.println(map.lastKey() + " " + map.firstKey());
}
}
}
}
25 changes: 25 additions & 0 deletions Vinjunho/boj/7662/boj7662.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import sys
import heapq

input = sys.stdin.readline
T = int(input())
for _ in range(T):
n = int(input())
nq, mq = [], []
visit = [False] * (n + 1) # 지워진 번호 (입력된 순서)
for i in range(n):
command, num = input().split()
num = int(num)
if command == 'I': # 입력 명령이면
heapq.heappush(nq, (num, i)) # 작은수우선
heapq.heappush(mq, (-num, i)) # 큰수우선
continue
elif num == 1 and mq: # 지우라는 명령어에 지울 값이 있으면
visit[heapq.heappop(mq)[1]] = True # 입력된 값을 지우고 지웠음을 저장한다
elif num == -1 and nq: # 지우라는 명령어에 지울 값이 없으면
visit[heapq.heappop(nq)[1]] = True # 입력된 값을 지우고 지웠음을 저장한다
while nq and visit[nq[0][1]]: # nq에서 제거할 수 있을만큼 제거하겠다.
heapq.heappop(nq)
while mq and visit[mq[0][1]]: # mq에서 제거할 수 있을만큼 제거하겠다.
heapq.heappop(mq)
print(f'{-mq[0][0]} {nq[0][0]}' if nq else 'EMPTY')