ACM ICPC 2019 예선 풀이

Dev2019-10-12

개요

그냥 ACM ICPC 2019 예선에서 제가 풀었던 문제들의 풀이입니다. 못 푼 문제는… 언젠간 풀겠죠?

B - Balanced String

길이가 n인 균형잡힌 문자열이라는 녀석의 수를 세는 문제입니다. 문자열은 0과 1로 이루어져 있는데, 첫번째 문자를 포함하는 모든 부분 문자열의 0과 1의 개수의 차이가 1 이하인 문자열을 균형잡힌 문자열이라고 부른다고 합니다.

규칙을 찾아보자면 다음과 같습니다.

길이가 1인 경우: 0, 1

길이가 2인 경우: 01, 10

길이가 3인 경우: 010, 011, 100, 101

길이가 4인 경우: 0101, 0110, 1001, 1010

기존에 균형잡힌 문자열이 있을 때, 문자열의 오른쪽 끝에 문자가 하나 추가돼서 길이가 하나씩 늘어난다고 생각해 봅시다.

n이 하나 증가하여 짝수에서 홀수가 되는 경우는 이미 0과 1의 개수가 같으므로 0이나 1 중 아무거나 추가되더라도 균형잡힌 문자열입니다. 따라서 경우의 수가 2배가 됩니다.

반대로 n이 하나 증가하여 홀수에서 짝수가 되는 경우는 다릅니다. 이미 0과 1의 개수의 차이가 1 나기 때문에, 한가지 선택지밖에 없습니다. 따라서 경우의 수는 n이 증가하기 전과 같습니다.

이 사실을 가지고 식을 세워보게 되면, 2^(⌊(n+1)/2⌋)가 길이 n인 균형잡힌 문자열의 수가 됩니다.

문제에선 오버플로우 때문에 16769023로 나눈 나머지를 출력하도록 하고 있으며, 어렵지 않게 다음과 같이 구현할 수 있습니다.

#include <iostream>

using namespace std;

int main() {
  int n, ans = 1;
  cin >> n;
  n++;
  n /= 2;
  for (int i = 0; i < n; ++i) {
    ans *= 2;
    ans %= 16769023;
  }
  cout << ans << "\n";
  return 0;
}

C - Byte Coin

간단한 시뮬레이션 문제입니다. 어렵게 생각할 것 없이, 다음 날 가격이 오르면 사고, 다음 날 가격이 떨어지면 팔면 됩니다. 마지막 날에 코인 다 팔아서 돈으로 바꾸고 출력.

#include <iostream>

using namespace std;

int main() {
  int n, price[15];
  long long w, coin = 0;

  cin >> n >> w;
  for (int i = 0; i < n; ++i)
    cin >> price[i];

  for (int i = 0; i < n - 1; ++i) {
    if (price[i] < price[i + 1]) {
      coin += w / price[i];
      w %= price[i];
    } else if (price[i] > price[i + 1]) {
      w += coin * price[i];
      coin = 0;
    }
  }

  w += coin * price[n - 1];
  cout << w << "\n";
}

H - Four Squares

백준과 명세가 다릅니다. 어디를 바꿔야 할까요?

어떤 자연수 n을 가장 적은 제곱수로 표현할 수 있는지를 묻는 문제입니다.

단순히 큰 제곱수부터 빼가는 것만으로는 문제를 풀 수 없습니다.

예를 들면 12 = 2^2 + 2^2 + 2^2니 3개의 제곱수로 이루어졌다고 할 수 있지만, 큰 제곱수부터 빼게 되면 3^3 + 1^1 + 1^1 + 1^1이므로 4가 되겠죠.

재귀 함수를 통해 최소값을 구하는 함수를 만들었습니다. 물론 재귀로 작성된 피보나치 함수와 같이, 반복 연산이 많이 일어나기 때문에, 메모이제이션 기법을 적용해 주어야 시간 제한 안에 풀 수 있습니다.

#include <algorithm>
#include <iostream>

using namespace std;

int sq[225];
int dp[50001];

int solve(int n) {
  if (n == 0)
    return 0;

  int &ret = dp[n];
  if (ret != -1)
    return ret;

  ret = 987654321;

  for (int i = 1; i < 225; ++i) {
    if (n < sq[i])
      continue;
    if (n == sq[i]) {
      ret = 1;
      return ret;
    }
    ret = min(ret, 1 + solve(n - sq[i]));
  }

  return ret;
}

int main(void) {
  for (int i = 0; i < 225; ++i)
    sq[i] = (225 - i) * (225 - i);
  for (int i = 0; i <= 50000; ++i)
    dp[i] = -1;

  int n;
  cin >> n;

  cout << solve(n) << "\n";
  return 0;
}

잡설

대회라고 긴장상태로 코드를 짰다 보니, 이 코드들을 대회가 끝나고 처음 열어봤을 때 말도 아니더군요. 대회도 많이 하다 보면 긴장을 안하게 되는 날이 오려는지.