안경잡이개발자

728x90
반응형

대회 링크: http://codeforces.com/contest/1335

A번 문제: http://codeforces.com/contest/1335/problem/A

 

  n개의 사탕을 두 사람이 나눠야 한다. 두 사람이 가지는 사탕의 수를 각각 a와 b라고 하자. 이 때 a > 0, b > 0, a > b가 되도록 하는 경우의 수를 출력하는 문제다.

 

[ 문제 해설 ]

 

  예를 들어, 다음과 같은 n들을 고려해보자.

 

  n = 1일 때는 조건에 맞게 나눌 수 있는 경우가 없다.

  n = 2일 때는 조건에 맞게 나눌 수 있는 경우가 없다.

  n = 3일 때는 (2, 1)로 나눌 수 있으므로 경우의 수는 1이다.

  n = 4일 때는 (3, 1)로 나눌 수 있으므로 경우의 수는 1이다.

  n = 5일 때는 (3, 2), (4, 1)로 나눌 수 있으므로 경우의 수는 2다.

  n = 6일 때는 (4, 2), (5, 1)로 나눌 수 있으므로 경우의 수는 2다.

  n = 7일 때는 (4, 3), (5, 2), (6, 1)로 나눌 수 있으므로 경우의 수는 3이다.

 

  결과적으로 경우의 수는 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, ... 순으로 나열되는 것을 알 수 있다. 따라서 단순히 (n - 1)를 2로 나눈 몫을 출력하면 그것이 정답이다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    print((n - 1) // 2)

 

B번 문제: http://codeforces.com/contest/1335/problem/B

 

  길이가 n인 문자열 s를 만드는데, 길이가 a인 모든 부분 문자열들이 b개의 서로 다른 문자들로 구성되도록 하는 문제다. 조건만 만족한다면 길이가 n인 문자열 s를 아무거나 출력해도 된다.

 

  예를 들어 n = 7, a = 5, b = 3일 때를 고려해보자. 이 때는 tleelte가 정답이 될 수 있다.

 

[ 문제 해설 ]

 

  이 문제는 길이가 a인 부분 문자열 k를 만들어서 순환적으로 출력하면 된다. n = 7, a = 5, b = 3일 때를 생각해보자.

 

  k = ['a', 'b', 'c', 'a', 'a']로 만들게 되면, 예를 순환적으로 출력하면 다음과 같다.

 

  {'a', 'b', 'c', 'a', 'a', 'a', 'b', 'c', 'a', 'a', 'a', 'b', 'c', 'a', 'a', ...}

 

  이렇게 되면, 길이가 5인 어떤 부분 문자열도 3개의 서로 다른 문자로 구성된다. 즉, 문제 해결의 아이디어는 앞에서부터 b만큼은 'a'부터 시작해 차례대로 문자를 담고, 나머지는 그냥 'a'만 나열하도록 문자열 k를 만든다. 이후에 k를 순환적으로 출력하면 정답이다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n, a, b = map(int, input().split())
    k = []
    
    # 앞의 b만큼은 'a'부터 시작해 차례대로 문자를 담기
    for i in range(b):
        k.append(chr(ord('a') + i))
    # 나머지는 그냥 'a'만 나열하도록 문자를 담기
    for i in range(a - b):
        k.append('a')
  
    # 이제 문자열 k를 순환적으로 출력하면 정답
    for i in range(n):
        print(k[i % a], end='')
    print()

 

C번 문제: http://codeforces.com/contest/1335/problem/C

 

  n명의 학생을 두 팀으로 나눌 때, 첫 번째 팀은 모두 스킬이 다르고 두 번째 팀은 모두 스킬이 같도록 구성하고자 한다. 단, 두 팀의 인원이 동일해야 한다. 이 때 팀의 가능한 최대 인원 수를 찾는 문제다.

 

  예를 들어 n = 7이고, 학생들의 스킬이 {4, 2, 4, 1, 4, 3, 4}라면 다음과 같이 나누었을 때 각 팀의 인원수가 제일 많다.

 

  첫 번째 팀: {1, 2, 3}

  두 번째 팀: {4, 4, 4}

 

  이 때 팀의 인원수는 3이다.

 

[ 문제 해설 ]

 

  이 문제는 각 스킬의 번호마다 등장 횟수를 체크(count_map)하고, 스킬의 번호가 총 몇 개 있는지 체크(distinct_count)하여 해결할 수 있다.

 

  예를 들어 위의 예시에서는 각 스킬의 번호마다 등장 횟수가 다음과 같다.

 

  1번 스킬: 1명

  2번 스킬: 1명

  3번 스킬: 1명

  4번 스킬: 3명

 

  또한 전체 스킬의 번호가 총 4개가 된다.

 

  이제 각 스킬 번호에 대해서, 그 스킬을 가진 학생들만을 오른쪽 팀에 두는 경우들을 고려하면 된다. 다만, 한 명의 학생은 왼쪽 팀에 넘겨 주어도 괜찮다. 이를 이용해 정답 코드를 작성할 수 있다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    data = list(map(int, input().split()))
    result = 0

    count_map = {}
    distinct_count = 0
    for i in data:
        if i not in count_map:
            count_map[i] = 1
            distinct_count += 1
        else:
            count_map[i] += 1
  
    # 각 스킬 번호를 확인하며
    for (key, value) in count_map.items():
        # 해당 스킬을 가진 학생을 두 번째 팀에 두는 경우를 고려하여, 최대 인원 수 찾기
        now = max(min(count_map[key], distinct_count - 1), min(count_map[key] - 1, distinct_count))
        result = max(result, now)
    print(result)

 

D번 문제: http://codeforces.com/contest/1335/problem/D

 

  이 문제는 전형적인 스도쿠 퍼즐이 무엇인지 알아야 한다. 다음과 같이 이미 완성된 스도쿠 보드가 있다고 해보자.

 

 

  이 때 안티 스도쿠란, 다음의 조건을 만족하는 보드를 말한다.

 

  1) 각 행에 대하여 최소한 두 개의 원소가 같아야 한다.

  2) 각 열에 대하여 최소한 두 개의 원소가 같아야 한다.

  3) 모든 3 x 3 블럭(Block)에서 최소한 두 개의 원소가 같아야 한다.

 

  이번 문제는 완성된 스도쿠 보드가 입력되었을 때, 안티 스도쿠를 만들어 출력하는 문제다.

 

[ 문제 해설 ]

 

  깊게 고민하면 문제 해결 아이디어를 떠올릴 수 있다. 다양한 방법이 있을 수 있겠지만, 저자는 다음과 같이 풀었다. 바로 다음의 9가지 위치에 있는 원소들의 값을 기존의 값이 아닌 다른 아무 값으로나 바꾸면 된다.

 

 

  따라서 9개의 칸에 있는 각 원소의 값이 1인 경우에는 2로 바꾸고, 1이 아닌 경우에는 1로 바꾸면 된다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    data = []
    for i in range(9):
        data.append(list(map(int, input())))

    # 9가지 위치의 값을 기존의 값이 아닌 다른 값으로 변경
    data[0][0] = 2 if data[0][0] == 1 else 1
    data[1][3] = 2 if data[1][3] == 1 else 1
    data[2][6] = 2 if data[2][6] == 1 else 1
    data[3][1] = 2 if data[3][1] == 1 else 1
    data[4][4] = 2 if data[4][4] == 1 else 1
    data[5][7] = 2 if data[5][7] == 1 else 1
    data[6][2] = 2 if data[6][2] == 1 else 1
    data[7][5] = 2 if data[7][5] == 1 else 1
    data[8][8] = 2 if data[8][8] == 1 else 1
  
    for i in range(9):
        print(''.join(map(str, data[i])))
728x90
반응형

728x90
반응형

대회 링크: http://codeforces.com/contest/1343

A번 문제: http://codeforces.com/contest/1343/problem/A

 

  정수 n이 주어졌을 때, 정수 k가 1보다 큰 조건 하에서 다음을 만족시키는 양수 x가 하나라도 있는지 찾는 문제다.

 

 

[ 문제 해설 ]

 

  이 문제는 완전 탐색(Brute Forcing)으로 해결할 수 있다. k를 2부터 쭉 증가시켜보자. n은 다음과 같이 표현된다.

 

  3x = n

  7x = n

  15x = n

  31x = n

  63x = n

  ...

 

  보면 x의 계수가 기하급수적으로 증가한다. 이 때 n은 최대 1,000,000,000이기 때문에, 모든 경우의 수를 고려해도 빠르게 문제를 풀 수 있다. (대략 k가 30만 되어도 계수는 10억에 근접할 것이다.)

 

  그래서 3x, 7x, 15x, 31x, 63x, ... 이렇게 증가 시키면서, n이 각 계수로 나누어 떨어지는지 확인한다. 만약 나누어 떨어진다면 그 때의 x를 출력하면 정답이다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    coefficient = 2
    now = 3 # now는 3, 7, 15, 31, ...와 같이 증가한다.
    while True:
        # n이 now로 나누어 떨어진다면
        if n % now == 0:
            # 그 몫을 출력한다.
            print(n // now)
            break
        coefficient *= 2
        now += coefficient

 

B번 문제: http://codeforces.com/contest/1343/problem/B

 

  배열의 크기 n이 주어졌을 때, 이 배열의 앞 부분 절반은 짝수 원소로만 구성되고, 뒷 부분 절반은 홀수 원소로만 구성된다고 한다. 이 때 모든 원소들은 서로 다르다. 앞 부분의 합과 뒷 부분의 합이 동일하게 되는 리스트를 하나라도 찾으면 되는 문제다.

 

  예를 들어 N = 4일 때, 2 4 1 5를 찾으면 정답이다. N = 8일 때는 2 4 6 8 1 3 5 11를 찾으면 정답이다.

 

[ 문제 해설 ]

 

  이 문제는 홀수 합의 특징을 생각하면 어렵지 않게 풀 수 있다. 홀수를 홀수 번 더하면 홀수이다. 즉, N / 2가 홀수라면 리스트를 만들 수 없다. 예를 들어 N = 6 이라면, 리스트를 만들 수 없다. 즉 N이 4의 배수일 때만 리스트를 만들 수 있다. 그렇다면 N이 4의 배수일 때는 리스트를 어떻게 만들면 될까?

 

  앞 부분 절반은 그냥 2부터 하나씩 나열한다. 뒷 부분 절반은 앞 부분에서 1씩 더하거나 빼서 만들 수 있다. (정확히는 뒷 부분 절반의 합이 앞 부분 절반의 합이 되어야 하므로, 뒷 부분 절반을 또 절반으로 나누어서 처리하면 된다.)

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    # N이 4의 배수일 때 리스트를 만들 수 있다.
    if n % 4 != 0:
        print("NO")
    else:
        print("YES")
        front = []
        # 앞 부분 절반(짝수 부분)은 2부터 하나씩 출력
        for i in range(1, n // 2 + 1):
            print(i * 2, end=' ')
            front.append(i * 2)
        # 뒷 부분 절반(홀수 부분)의 절반은 -1씩 해서 출력
        for i in range(n // 4):
            print(front[i] - 1, end=' ')
        # 뒷 부분 절반(홀수 부분)의 남은 절반은 +1씩 해서 출력
        for i in range(n // 4, n // 2):
            print(front[i] + 1, end=' ')
        print()

 

C번 문제: http://codeforces.com/contest/1343/problem/C

 

  리스트가 주어졌을 때, 리스트의 원소를 앞에서부터 하나씩 확인하며 원소를 고른다. 단, 양수와 음수를 번갈아 가면서 원소를 하나씩 골라야 한다. 처음에 양수를 골랐으면, 다음 번에는 음수를 골라야 한다. 반대로 처음에 음수를 골랐으면, 다음 번에는 양수를 골라야 한다.

 

  이 때, 원소들을 골라서 만들어지는 가장 긴 부분수열 중에서 원소의 합이 가장 큰 것을 찾는 것이 요구사항이다.

 

  예를 들어 수열이 [-2, 8, 3, 8, -4, -15, 5, -2, -3, 1]이라면, 정답 부분수열은 [-2, 8, -4, 5, -2, 1]이 된다. 이 부분 수열의 합은 6이다.

 

[ 문제 해설 ]

 

  이 문제는 같은 부호를 가지는 데이터들을 묶어서 해결할 수 있다. 예를 들어 [-2, 8, 3, 8, -4, -15, 5, -2, -3, 1]라는 수열이 있다면, 이를 다음과 같이 인접한 원소끼리 묶도록 한다.

 

  [-2, 8, 3, 8, -4, -15, 5, -2, -3, 1]

 

  이 때, 같은 묶음 중에서 가장 큰 수를 각각 뽑으면 된다. 이 때 합은 6이 된다.

 

  [-28-45-21]

 

  이를 코드로 옮기면 정답이다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    data = list(map(int, input().split()))
    result = 0

    # 시작점이 양수인지 음수인지 기록(양수: 1, 음수: -1)
    if data[0] > 0:
        sign = 1
    else:
        sign = -1
  
    # 같은 부호들을 담을 리스트
    array = []
 
    # 리스트의 원소를 하나씩 확인하며
    for x in data:
        # 현재 양수일 때 양수가 나온 경우
        if sign == 1 and x > 0:
            # 같은 부호이므로 담기
            array.append(x)
        # 현재 양수일 때 음수가 나온 경우
        if sign == 1 and x < 0:
            # 현재 부호를 음수로 변환
            sign = -1
            result += max(array)
            array = [x]
        # 현재 음수일 때 음수가 나온 경우
        if sign == -1 and x < 0:
            # 같은 부호이므로 담기
            array.append(x)
        # 현재 음수일 때 양수가 나온 경우
        if sign == -1 and x > 0:
            # 현재 부호를 양수로 변환
            sign = 1
            result += max(array)
            array = [x]
  
    result += max(array)
    print(result)

 

D번 문제: http://codeforces.com/contest/1343/problem/D

 

  길이가 n인 수열이 주어진다. (n은 짝수) 이 수열의 모든 원소는 k 이하의 값을 가진다.

 

  이 때 대칭되는 원소들의 합을 모두 x로 만들고자 한다. 이를 위해 원소를 하나씩 골라서 그 원소의 값을 k 이하의 수로 바꾸는 연산을 수행할 수 있다. 결과적으로 최소 연산의 횟수를 출력하면 되는 문제이다.

 

  예를 들어 [1, 2, 1, 2]의 경우 대칭되는 원소의 합이 각각 3, 3이다. 따라서 x = 3으로 설정했을 때, 연산이 필요하지 않다. 따라서 최소 연산 횟수는 0이다.

 

  또한 [12, 2, 1]의 경우 대칭되는 원소의 합이 각각 4, 2이다. 따라서 x = 4으로 설정했을 때, 첫 번째 원소를 3으로 변경하면 된다. 최소 연산 횟수는 1이다.

 

  또한 [6, 1, 1, 7, 6, 3, 4, 6]의 경우 대칭되는 원소의 합이 각각 13, 4, 5, 12이다. 따라서 x = 8로 설정했을 때, 최소 연산 횟수로 4가 나올 수 있다.

 

[ 문제 해설 ]

 

  이 문제는 아이디어만 잘 떠올리면 쉽게 해결할 수 있는 문제다. 일단 각 대칭 쌍마다 하나의 원소를 변경할 수 있다고 가정해보자. 예를 들어 3번째 예제를 가지고 생각해보자.

 

  [6, 1, 1, 7, 6, 3, 4, 6]를 확인하면, 각 대칭 쌍을 정렬하여 나열하면 다음과 같다. 총 네 개의 쌍이 존재한다.

 

  (1, 3): 합 = 4

  (1, 4): 합 = 5

  (6, 6): 합 = 12

  (6, 7): 합 = 13

 

  이 때 각 대칭 쌍의 원소 중 하나의 값을 변경할 수 있을 때의 범위는 다음과 같다.

 

  (1, 3) → [2, 10]

  (1, 4) → [2, 11]

  (6, 6) → [7, 13]

  (6, 7) → [7, 14]

 

  결과적으로 모든 쌍들의 [lower_bound, upper_bound] 범위를 고려해보자. x를 2부터 2 * n까지 증가시키면서 확인한다고 생각해보자. x = 2라고 하면 (1, 3)과 (1, 4)의 범위에 포함되어 있으므로, 이들은 원소를 1개씩만 바꾸면 된다. 나머지 두 쌍은 원소를 2개 모두 바꾸어야 한다. 즉, x = 2일 때 최소 연산 횟수는 6이 될 것이다. 만약에 x = 7이라면 어떨까? 모든 대칭 쌍이 원소를 1개씩만 바꾸면 된다. 이 때 최소 연산 횟수는 4가 된다. 따라서 x를 증가 시키면서 매 x에 대하여 lower_bound와 upper_bound를 고려해주면 된다.

 

  사실 [2, 2 * n] 범위에 대해서 x를 모두 고려할 필요는 없다. 각 대칭 쌍에 대하여 ① 대칭 쌍의 합, ② lower_bound, upper_bound 세 가지에 대해서만 고려하면 된다. 이들을 포인트(points)라고 하겠다. 포인트는 오름차순 정렬이 되어 있어야 한다.

 

  결과적으로 포인트를 하나씩 확인할 것이다. 이를 위해 lower_bound의 개수를 in_map에 기록하고, upper_bound의 개수를 out_map에 기록한다. 또한 대칭 쌍의 합은 sum_map에 기록하도록 하자. 이제 최소 연산 횟수를 구할 수 있다.

 

points 2 4 5 7 10 11 12 13 14
in_map 2     2          
out_map         1 1   1 1
sum_map   1 1       1 1  
연산 횟수 6 5 5 4 4 5 5 5 7

 

  결과적으로 최소 연산 횟수는 4가 되는 것이다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n, k = map(int, input().split())
    data = list(map(int, input().split()))
  
    # 3개의 해시 맵(Hash Map)을 사용합니다.
    in_map = {}
    out_map = {}
    sum_map = {}

    # x의 값으로 확인해야 할 포인트들
    points = set()
 
    for i in range(n // 2):
        min_value = min(data[i], data[n - i - 1])
        max_value = max(data[i], data[n - i - 1])

        # lower_bound, upper_bound, 대칭 쌍의 합 계산
        lower_bound = min_value + 1
        upper_bound = max_value + k
        sum_value = min_value + max_value

        # 모든 lower_bound를 in_map에 기록합니다.
        if lower_bound in in_map:
            in_map[lower_bound] += 1
        else:
            in_map[lower_bound] = 1

        # 모든 upper_bound를 out_map에 기록합니다.
        if upper_bound in out_map:
            out_map[upper_bound] += 1
        else:
            out_map[upper_bound] = 1

        # 모든 대칭 쌍의 합을 sum_map 기록합니다.
        if sum_value in sum_map:
            sum_map[sum_value] += 1
        else:
            sum_map[sum_value] = 1

        points.add(lower_bound)
        points.add(upper_bound)
        points.add(sum_value)
  
    points = sorted(points)
 
    now_sum = 0
    result = n
  
    for i in points:
        # lower_bound의 개수만큼 더하기
        in_count = 0
        if i in in_map:
            in_count = in_map[i]
        now_sum += in_count

        # sum의 개수 구하기
        sum_count = 0
        if i in sum_map:
            sum_count = sum_map[i]

        # 최소 연산 횟수 계산
        now_total = n - (now_sum + sum_count)
        result = min(result, now_total)

        # upper_bound의 개수만큼 빼기
        out_count = 0
        if i in out_map:
            out_count = out_map[i]
        now_sum -= out_count
    
    print(result)
728x90
반응형