https://programmers.co.kr/learn/courses/30/lessons/12936#
문제설명
접근 방법1
n
에 대한 제한이 20으로 엄청 관대해보였다. 따라서 시간복잡도를 생각하지 않고 바로 생각나는 next_permutation
으로 풀이했다.
next_permutation
으로 나올때 마다 카운트하여 k
번째 순열이 나올때 그 값을 저장하여 type을 바꿔주어 answer
에 저장하였다.
하지만... 시간초과였다. n
이 20일 때 20!
이 엄청난 수 일줄 생각못했다.
다른 방법의 힌트를 얻자면 모든 순열을 알 필요 없이 사전 순서로 나열할 때 k
번째 순열만 알 면 된다.
접근 방법2
먼저 vector<int> num
에 1~20을 오름차순으로 넣어둔다.
예제를 보자
1,2,3
1,3,2
2,1,3
2,3,1
3,1,2
3,2,1
의 순열로 나열되고 k=5
일 때, 앞자리를 (n-1)!
로 나누면 몇번째 수가 오는지 알 수 있다.
k-1/(n-1)!
는 4/2 = 2 이고 2번째 index
에 있는 num
을 answer
에 넣어준다.
여기서 k-1
을 하는 것은 index
를 맞추기 위함이다. (이거 생각하는데 오래걸림...)
이후 k
값을 조정해 주어야 하는데 index
값으로 mod
연산하여 나온 값으로 조정해 준다.
3,1,2
3,2,1
첫 연산에 의해 index
가 2인 num[2]
즉, 앞자리가 3인 위의 두 순열로 선택된다.
이후 (n-1)!
로 mod
연산을 하면 남은 순열의 몇 번째 수 인지 알 수있다. 단, mod
연산으로 나온 결과가 0이면 (n-1)!
번째 수 이므로 k = (n-1)!
로 설정해준다.
특정 num[i]
을 answer
에 넣어줄 때 num[i]
를 vector에서 삭제해 주면 다음 수를 넣을 때 계산이 편리하다.
코드
#include <string>
#include <vector>
using namespace std;
long long fac(int a){
long long ans = 1;
for(int i=1;i<=a;i++){
ans*=i;
}
return ans;
}
vector<int> solution(int n, long long k) {
vector<int> answer;
vector<int> num;
long long now=k;
int cnt=1;
for(int i=1;i<=20;i++){
num.push_back(i);
}
while(cnt != n){
long long tmp = fac(n-cnt);
int idx = (now-1) / tmp;
answer.push_back(num[idx]);
num.erase(num.begin()+idx);
cnt++;
now %= tmp;
if (now == 0)
now = tmp;
}
answer.push_back(num[0]);
return answer;
}
결과
후기
k
와 n
, 그리고 factorial
을 갖고 노는 수학문제 성향이 강했다.
수학적 논리를 구하는데 조금 시간을 들였지만, 코드로 옮기는 구현에서는 큰 어려움이 없었다.