코딩테스트/백준

[백준][C++]1256번 사전

윤깡패 2023. 10. 6. 11:14

문제

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

 

1256번: 사전

동호와 규완이는 212호에서 문자열에 대해 공부하고 있다. 김진영 조교는 동호와 규완이에게 특별 과제를 주었다. 특별 과제는 특별한 문자열로 이루어 진 사전을 만드는 것이다. 사전에 수록되

www.acmicpc.net

 

 

풀이

  • a와 z로 만들 수 있는 모든 경우의 수

= N + M개에서 N개를 뽑는 경우의 수 = N + M개에서 M개를 뽑는 경우의 수

 

  • 조합 점화식

D[i][j] = D[i - 1][j] + D[i - 1][j - 1]

 

  • 문자 선택 기준
    • (T : 현재 자릿수에서 a를 선택했을 때 남은 문자로 만들 수 있는 모든 경우의 수)
    • T ≥ K : 현재 자리 문자를 a로 선택
    • T < K : 현재 자리 문자를 z로 선택, K = K - T로 업데이트

 

조합(Combination) 알고리즘

 

조합(Combination) 알고리즘

순열과 조합 핵심 이론 순열(Permutation) : 서로 다른 n개의 숫자에서 r개를 선택하여 순서를 고려해 나열할 경우의 수 (순서 고려 O) 조합(Combination) : 서로 다른 n개의 숫자에서 서로 다른 r개를 선택

soobin0821.tistory.com

 

 

코드

#include <iostream>
#include <vector>
using namespace std;

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  int N, M, K;
  cin >> N >> M >> K;

  // 조합 테이블 초기화
  int DP[202][202] = {0};
  for(int i = 1; i <= N + M; i++)
  {
    DP[i][0] = 1;
    DP[i][1] = i;
    DP[i][i] = 1;
    for(int j = 2; j < i; j++)	// 고르는 수의 개수가 전체 개수를 넘을 수 없음
    {
      DP[i][j] = min(DP[i - 1][j - 1] + DP[i - 1][j], 1000000001);	// K 범위가 넘어가면 범위 최댓값 지정
    }
  }

  if(DP[N + M][N] < K)	// 주어진 자릿수로 만들 수 없는 K번째 수이면
  {
    cout << "-1";	// -1 출력하기
    return 0;
  }

  while(1)
  {
    if(DP[N + M - 1][N - 1] >= K)	// a를 선택했을 때 남은 문자로 만들 수 있는 모든 경우의 수가 K보다 크면
    {
      cout << "a";	// a 출력하기
      N--;	// a 문자 개수 감소
      if(N == 0)
      {
        while(M)
        {
          cout << "z";
          M--;
        }
        break;
      }
    }
    else	// 모든 경우의 수가 K보다 작으면
    {
      cout << "z";	// z 출력하기
      K -= DP[N + M - 1][N - 1];	// K값 업데이트
      M--;	// z 문자 개수 감소
      if(M == 0)
      {
        while(N)
        {
          cout << "a";
          N--;
        }
        break;
      }  
    }
  }  

  return 0;
}

 

 

오답

조합 테이블 초기화 시, K 범위가 넘어가면 범위 최댓값 저장

 

더보기
#include <iostream>
using namespace std;

static int N, M, K;
static long D[202][202];

int main() 
{
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  cin >> N >> M >> K;

  // 조합 테이블 초기화
  for(int i = 0; i <= 200; i++)
  {
    for(int j = 0; j <= i; j++)
    {
      if(j == 0 || j == i)
      {
        D[i][j] = 1;
      }
      else
      {
        D[i][j] = D[i - 1][j - 1] + D[i - 1][j];
        // K 범위가 넘어가면 범위 최댓값 저장
        if(D[i][j] > 1000000000)
        {
          D[i][j] = 1000000001;
        }
      }
    }
  }
  if(D[N + M][M] < K)  // 주어진 자릿수로 만들 수 없는 K번째 수이면
  {
    cout << "-1";
  }
  else
  {
    while(!(N == 0 && M == 0))  
    {
      if(D[N - 1 + M][M] >= K)	// a를 선택했을 때 남은 문자로 만들 수 있는 모든 경우의 수가 K보다 크면
      {
        cout << "a";
        N--;
      }
      else	// 모든 경우의 수가 K보다 작으면
      {
        cout << "z";
        K = K - D[N - 1 + M][M];  // K값 업데이트
        M--;
      }
    }
  }

}

 

[ 정답코드 예시 ]

규완이의 사전에 수록돼 있는 문자열의 개수

= a와 z로 만들 수 있는 모든 경우의 수

= DP[N + M][N] = DP[N + M][M]

 

 

 

 

 

728x90