https://www.acmicpc.net/problem/18870

 

18870번: 좌표 압축

수직선 위에 N개의 좌표 X1, X2, ..., XN이 있다. 이 좌표에 좌표 압축을 적용하려고 한다. Xi를 좌표 압축한 결과 X'i의 값은 Xi > Xj를 만족하는 서로 다른 좌표의 개수와 같아야 한다. X1, X2, ..., XN에 좌

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 N의 범위가 1,000,000 이므로 O(N^2)의 알고리즘으로는 시간 초과 판정을 받기 때문에 logN의 시간복잡도를 가지고 있는 이진탐색으로 해결할 수 있습니다. 문제의 예시를 설명하면 다음과 같습니다.

 

2 4 -10 4 -9

2 보다 작은 수는 -10, -9 총 2개 입니다.

4보다 작은 수는 -10, -9, 2 총 3개 입니다.

-10보다 작은수는 없습니다. 총 0개 입니다.

4보다 작은 수는 -10, -9, 2 총 3개 입니다.

-9보다 작은 수는 -10 총 1개 입니다.

따라서 결과는 2, 3, 0, 3, 1 입니다.

 

다만 두번째 예시에서 알 수 있듯이 중복된 숫자도 고려해줘야 합니다. 즉 다음과 같습니다.

1000 999 1000 999 1000 999

1000 보다 작은 수는 999 총 1개 입니다.

999보다 작은 수는 없습니다. 총 0개 입니다.

1000 보다 작은 수는 999 총 1개 입니다.

999보다 작은 수는 없습니다. 총 0개 입니다.

1000 보다 작은 수는 999 총 1개 입니다.

999보다 작은 수는 없습니다. 총 0개 입니다.

따라서 결과는 1, 0, 1, 0, 1, 0 입니다.

즉 이를 통해서 중복을 제거해줘야 하므로 set 즉 집합을 사용해야하는것을 알 수 있고 집합을 사용하여 정렬한 뒤 이진탐색을 통해 해당 위치를 찾으면 됩니다. 소스코드는 다음과 같습니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def find(num, start, last):
  while last >= start:
    mid = (start + last) // 2
    if data2[mid] == num:
      return mid
    elif data2[mid] > num:
      last = mid - 1
    else:
      start = mid + 1
 
= int(input())
data = list(map(int, input().split()))
data2 = set(data)
data2 = list(data2)
data2.sort()
 
 
for i in range(n):
  print(find(data[i], 0len(data2)-1), end=" ")
cs

https://www.acmicpc.net/problem/11723

 

11723번: 집합

첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 알고리즘의 분류상 구현 문제로 문제에서 주어진 대로만 차분하게 구현하면 쉽게 해결할 수 있는 문제였습니다. 다만 실버 5 티어라는 난이도에 비해 정답률이 29%로 낮은 편인데 그 이유는 메모리 제한 때문이라 생각됩니다. 저 역시 pypy3로 풀었을 때 메모리 초과 판정을 받았고 pypy는 시간 복잡도가 좋은 대신 공간 복잡도 측면에서 불리하다는 사실을 알게 되어 python3으로 해결한 결과 정답 판정을 받을 수 있었습니다. 문제의 조건을 그대로 구현하면 되는 문제이기 때문에 해결방안은 따로 제시하지 않습니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import sys
input = sys.stdin.readline
 
= int(input())
= set()
for i in range(n):
  a = input().split()
  if a[0== 'empty' or a[0== 'all':
    if a[0== 'all':
      s = set([1234567891011121314151617181920])
    elif a[0== 'empty':
      s = set()
  else:
    b = int(a[1])
    if a[0== 'add':
      s.add(b)
    elif a[0== 'check':
      if s.intersection(set([b])):
        print(1)
      else:
        print(0)
    elif a[0== 'remove':
      if s.intersection(set([b])):
        s.remove(b)
    elif a[0== 'toggle':
      if s.intersection(set([b])):
        s.remove(b)
      else:
        s.add(b)
cs

https://www.acmicpc.net/problem/14499

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지

www.acmicpc.net


문제 해결을 위한  과정

이 문제의 경우 골드 4 티어로 비교적 높은 난이도를 가지고 있지만 사실 구현으로 분류되는 문제의 특성상 차분하게 문제에서 제시한 조건을 따라가면 쉽게 해결할 수 있는 문제였습니다. 다만 주사위라는 공간지각 능력이 필요한 문제라서 조금 생각을 해야 합니다. 이 문제를 푸는 데 있어서 가장 중요한 것은 전개도입니다. 전개도 대로 주사위를 만들면 아래의 그림과 같습니다. 

윗면 1 아랫면 6 앞면 2 뒷면 5 왼쪽면 4 오른쪽면 3

이를 동쪽, 서쪽, 남쪽, 북쪽으로 돌리더라도 주사위의 특성상 마주보는 면의 합은 7입니다. 위 주사위 그림의 면에 배정된 숫자는 문제에서 주어진 조건 "주사위는 지도 위에 윗 면이 1이고, 동쪽을 바라보는 방향이 3인 상태로 놓여져 있으며, 놓여 있는 곳의 좌표는 (x, y)이다. 가장 처음에 주사위에는 모든 면에 0이 적혀져 있다." 를 반영한 것입니다. 즉 위치를 저렇게 잡은 후 모든 주사위의 면을 0으로 초기화합니다. 그 후 문제에서 테스트 케이스로 주어진 지도에 0행 0열에 주사위를 배치합니다. 첫 번째 예시에서 맵은 다음과 같습니다.

과정 및 주사위 그림은 다음과 같습니다.

1. 시작지점에 위치(0행 0열)

2. 명령이 4 이므로 남쪽으로 회전 1행 0열이 3 이므로 주사위의 밑면은 3, 윗면은 0이 됨 따라서 0 출력

 

3. 명령이 4 이므로 남쪽으로 회전 2행 0열이 5 이므로 주사위의 밑면은 5, 뒷면은 3, 윗면은 0이 됨 따라서 0 출력

4. 명령이 4 이므로 남쪽으로 회전 3행 0열이 7 이므로 주사위의 밑면은 7 뒷면은 5 윗면은 3이 됨 따라서 3 출력

5. 명령이 1 이므로 동쪽으로 회전 3행 1열이 8 이므로 주사위의 밑면은 8 왼쪽면은 7 오른쪽면은 3 뒷면은 5 윗면은 0 따라서 0 출력

이러한 과정을 반복한다.


문제 해결을 위한 팁

처음의 주사위의 전개도를 2차원 리스트의 형태로 구현한 후 문제를 해결하려 하였으나 예상보다 복잡해지는 바람에 다른 방법을 찾던 중 단순하게 일차원 리스트로 해결할 수 있음을 알게 되었고 해결방법은 다음과 같다. 먼저 리스트를 다음과 같이 설정한다.

dice = [0, 0, 0, 0, 0, 0]

윗면 1 아랫면 6 앞면 2 뒷면 5 왼쪽면 4 오른쪽면 3

그 후 이 그림을 보면서 항상 리스트의 0번째가 윗면, 1번째가 앞면, 2번째가 오른쪽면 이런 식으로 5번째가 아래쪽면이라는 것을 고정한 뒤 swap을 해준다. 즉 남쪽으로 한번 회전을 해주면 위의 그림에서 왼쪽 오른쪽면은 그대로 있고 나머지 4면이 회전을 하는 형태이다. 따라서 이를 이렇게 표현할 수 있다.

 

if 남쪽으로 회전할 시:

    dice[5], dice[1], dice[0], dice[4] = dice[1], dice[0], dice[4], dice[5] 

 

이를 전체의 경우에 적용하면 쉽게 해결할 수 있다. 소스코드는 아래와 같다.

소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def movePos(dir):
  if dir == 1:
    dice[5], dice[2], dice[0], dice[3= dice[2], dice[0], dice[3], dice[5]
  elif dir == 2:
    dice[5], dice[3], dice[0], dice[2= dice[3], dice[0], dice[2], dice[5]
  elif dir == 3:
    dice[5], dice[4], dice[0], dice[1= dice[4], dice[0], dice[1], dice[5]
  else:
    dice[5], dice[1], dice[0], dice[4= dice[1], dice[0], dice[4], dice[5]
 
n, m, x, y, k = map(int, input().split())
graph = []
for _ in range(n):
  graph.append(list(map(int, input().split())))
 
 
order = list(map(int, input().split()))
 
dice = [000000]
dx = [00-11]
dy = [1-100
 
for i in range(k):
  dir = order[i]
  nx = x + dx[dir-1]
  ny = y + dy[dir-1]
  if 0 > nx or 0 > ny or nx >= n or ny >= m:
    continue
  movePos(dir)
  if graph[nx][ny] == 0:
    graph[nx][ny] = dice[5]
  else:
    dice[5= graph[nx][ny]
    graph[nx][ny] = 0
  print(dice[0])
  x, y = nx, ny
cs

https://www.acmicpc.net/problem/2941

 

2941번: 크로아티아 알파벳

예전에는 운영체제에서 크로아티아 알파벳을 입력할 수가 없었다. 따라서, 다음과 같이 크로아티아 알파벳을 변경해서 입력했다. 크로아티아 알파벳 변경 č c= ć c- dž dz= đ d- lj lj nj nj š s= ž z=

www.acmicpc.net


문제 해결을 위한 과정

처음에 이 문제를 해결할 때 해당하는 모든 경우의 수를 처리하여 해결하였습니다. 그러나 조금 더 쉽게 해결할 수 있는 방법이 없을까 고민하다 구글링을 통해 새로운 풀이를 알게 되었습니다. 

즉 크로아티아 문제에 해당하는 문자들을 리스트로 구성한 후 해당하는 원소들이 입력받은 문자열에 존재하면 다른 문자로 변경한 후 변경된 문자열의 길이를 세면 됩니다. 소스코드는 다음과 같습니다.


소스코드
1
2
3
4
5
word = ['c=''c-''dz=''d-''lj''nj''s=''z=']
= input()
for i in word:
    s = s.replace(i, 'a')
print(len(s))
cs

 

https://www.acmicpc.net/problem/14659

 

14659번: 한조서열정리하고옴ㅋㅋ

첫째 줄에 봉우리의 수 겸 활잡이의 수 N이 주어진다. (1 ≤ N ≤ 30,000) 둘째 줄에 N개 봉우리의 높이가 왼쪽 봉우리부터 순서대로 주어진다. (1 ≤ 높이 ≤ 100,000) 각각 봉우리의 높이는 중복 없이

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 N이 100,000이기 때문에 이중 for문을 이용해서 풀게 되면 O(N^2) = 10,000,000,000 번의 연산을 최악의 경우 수행하게 됩니다. 따라서 이 문제의 경우 python3으로 이중 for문을 이용하면 제한시간 2초내에 풀 수 없습니다. (다만 pypy3로 제출하면 정답판정을 받게 되는데 데이터의 추가가 필요해 보입니다.) 따라서 이 문제는 O(N)으로 해결해야 하는 문제 입니다. 따라서 한번의 반복문으로 해결할 수 있는 방법을 찾아야 합니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
= int(input())
data = list(map(int, input().split()))
 
height = data[0]
ans = []
count = 0
 
for i in range(1, n):
  if data[i] < height:
    count += 1
  else:
    height = data[i]
    ans.append(count)
    count = 0
  
ans.append(count) # 내림차순으로 정렬된 경우
print(max(ans))
cs

https://www.acmicpc.net/problem/1080

 

1080번: 행렬

첫째 줄에 행렬의 크기 N M이 주어진다. N과 M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 행렬 A가 주어지고, 그 다음줄부터 N개의 줄에는 행렬 B가 주어진다.

www.acmicpc.net


 

문제 해결을 위한 과정

이 문제의 경우 규칙을 찾으려 노력하다가 규칙이 도저히 발견이 되지 않아서 단순하게 3*3 행렬을 뒤집는 함수를 만든 후 문제를 풀 수 있었습니다. N, M의 범위가 50 이하인 자연수이므로 O(N^2)로 조회하며 모든 좌표에 대해 3*3 행렬을 뒤집는다면 (다만 마지막에서 두 번째 세 번째 열까지 가능합니다.) 50 * 47 * 3* 3 이므로 대략 21150번의 연산을 수행해야 합니다. 파이썬의 경우 1초에 약 2천만 번의 연산을 할 수 있다고 알려져 있으므로 제한시간이 2초인 이 문제에서는 충분히 해결할 수 있는 방법입니다. 따라서 모든 경우의 수를 따져가며 브루트 포스의 방식으로 해결합니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def sumGraph(x, y):
  result = 0
  for i in range(x):
    for j in range(y):
      result += graph3[i][j]
  return result
 
def switch(x, y):
  for i in range(x, x+3):
    for j in range(y, y+3):
      if graph[i][j] == 0:
        graph[i][j] = 1
      else:
        graph[i][j] = 0
 
n, m = map(int, input().split())
graph = []
graph2 = []
graph3 = [[0* m for _ in range(n)]
count = 0
 
for i in range(n):
  graph.append(list(map(int, input())))
  
for i in range(n):
  graph2.append(list(map(int, input())))
 
if n < 3 and m < 3:
  for i in range(n):
    for j in range(m):
      if graph[i][j] == graph2[i][j]:
        graph3[i][j] = 0
      else:
        graph3[i][j] = 1
  if(sumGraph(n,m))==0:
    print(0)
  else:
    print(-1)
else:
  for i in range(n-2):
    for j in range(m-2):
      if graph[i][j] != graph2[i][j]:
        switch(i, j)
        count += 1
  for i in range(n):
    for j in range(m):
      if graph[i][j] == graph2[i][j]:
        graph3[i][j] = 0
      else:
        graph3[i][j] = 1
  if sumGraph(n, m) != 0:
    print(-1)
  else:
    print(count)
cs

https://www.acmicpc.net/problem/1946

 

1946번: 신입 사원

첫째 줄에는 테스트 케이스의 개수 T(1 ≤ T ≤ 20)가 주어진다. 각 테스트 케이스의 첫째 줄에 지원자의 숫자 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개 줄에는 각각의 지원자의 서류심사 성

www.acmicpc.net


문제 해결을 위한 과정

이 문제는 정렬을 이용해서 풀 수 있었던 그리디 알고리즘 문제입니다. 문제를 잘 읽어보면 어떤 사람을 탈락시켜야 하는지 어떤 사람을 선발할 수 있을지에 대해서 알 수 있습니다. 예를 들어 다음의 경우가 있다고 생각해봅시다.

(3, 4)

(2, 1)

(1, 3)

(4, 2)

이 경우 서류심사 성적을 기준으로 오름차순을 취하면 다음과  같이 정렬됩니다.

(1, 3)

(2, 1)

(3, 4)

(4, 2)

문제에서 적어도 하나가 다른 지원자보다 떨어지지 않는 자만 선발한다는 원칙을 세웠으므로 (3, 4), (4, 2)인 사람은 서류심사 성적, 면접시험 성적 모두 (2, 1)에 비해서 떨어집니다. 따라서 이 둘을 탈락시키면 합격시킬 수 있는 사람은 (1, 3), (2,1)로 두 명입니다. 


문제 해결을 위한 팁

위의 문제 해결을 위한 과정에서 보였듯이 먼저 서류심사 성적을 기준으로 정렬을 취합니다. 이렇게 뒤면 무조건 뒤에 정렬된 사람이 앞에 정렬된 사람보다 서류심사 성적이 낮기 때문에 면접 성적을 기준으로 이 사람을 합격시킬지 탈락시킬지 정할 수 있습니다. 즉 서류심사 등수를 기준으로 정렬을 수행하면 앞사람들 중 면접 등수가 높은 사람이 있다면 합격, 면접 등수가 낮은 사람이 있다면 탈락하면 되는 것입니다. 따라서 min값을 잡은 후 그 min값보다 낮은 등수의 사람은 탈락, min값보다 높은 등수의 사람은 합격을 시키면 됩니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys
input = sys.stdin.readline
tc = int(input())
for _ in range(tc):
  n = int(input())
  data = []
  for _ in range(n):
    a, b = map(int, input().split())
    data.append((a, b))
  data.sort(key = lambda x: x[1])
  min = data[0][0]
  
  count = 1
  for i in range(1, n):
    if data[i][0< min:
      min = data[i][0]
      count += 1
  print(count)
cs

https://www.acmicpc.net/problem/11866

 

11866번: 요세푸스 문제 0

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 1,000)

www.acmicpc.net


문제 해결을 위한 과정

이 문제는 구현 유형의 문제로서 어려운 문제는 아니었지만 문제를 해결하는 데 있어서 생각하는 방식이 중요한 문제였습니다. 문제에서 주어진 예시를 해결해보겠습니다.

다음의 그림은 예시를 푸는 과정입니다. 이 예시에는 N은 7, K는 3입니다.

그림1. 3번째 3 출력
그림2. 3번째 6 출력
그림3. 3번째 2 출력
그림4. 3번째 7 출력
그림5. 3번째 5 출력
그림6. 3번째 1 출력&amp;nbsp;
그림7. 3번째 4 출력


문제 해결을 위한 팁

이 문제는 위의 그림을 자세히 살펴보면 알 수 있듯이 k번째 원소가 아닌 것들은 다시 뒤에 붙여서 해결한다는 점입니다. 따라서 관찰을 통해 큐를 구현하면 해결할 수 있음을 알 수 있습니다. 파이썬에서 큐 보다 효율적인 deque를 이용해 popleft(), append()를 이용해 해결할 수 있습니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from collections import deque
 
n, k = map(int, input().split())
= deque()
 
for i in range(1, n+1):
  q.append(i)
 
print("<", end="")
= 0
while True:
  i += 1
  if i != k:
    v = q.popleft()
    q.append(v)
  else:
    v = q.popleft()
    if (q):
      print(v, end=", ")
      i = 0
    else:
      print(v, end=">")
      break
 
 
cs

 

 

+ Recent posts