이전에 풀었던 LIS문제와 상당히 관련이 있어 난이도에 비하여 오래 걸리진 않은 것 같습니다.
[KLIS] 문제 😒😒😒😒😒
어떤 정수 수열에서 0개 이상의 숫자를 지우면 이 수열의 부분 수열 (subsequence) 를 얻을 수 있다. 예를 들어 10 7 4 9 의 부분 수열에는 7 4 9, 10 4, 10 9 등이 있다. 단, 10 4 7 은 원래 수열의 순서와 다르므로 10 7 4 9 의 부분 수열이 아니다.
어떤 부분 수열이 _단조 증가_할 때 이 부분 수열을 증가 부분 수열 (increasing subsequence) 라고 하며, 이 중 가장 긴 것을 최대 증가 부분 수열 (LIS, longest increasing subsequence) 라고 한다. 예를 들어, 5 20 21 22 8 9 10 의 최대 증가 부분 수열은 5 8 9 10 이다.
어떤 수열에는 LIS 가 두 개 이상 있을 수 있다. 예를 들어, 4 5 6 1 2 3 의 LIS 는 두 개가 있다.
모든 숫자가 서로 다른 (중복 숫자가 없는) 수열이 주어질 때, 이 수열의 LIS 중 사전 순서대로 맨 앞에서 k번째 있는 LIS 를 출력하는 프로그램을 작성하시오.
입력
입력의 첫 줄에는 테스트 케이스의 수 C (<= 50) 가 주어진다. 각 테스트 케이스의 첫 줄에는 수열에 포함된 원소의 수 N (<= 500) 과 K 가 주어진다. K 는 32비트 부호 있는 정수에 저장할 수 있다. 그 다음 줄에 N개의 정수로 수열이 주어진다. 각 정수는 1 이상 100,000 이하이며, 같은 수는 두 번 등장하지 않는다.
주어진 수열의 LIS 는 최소 K 개 있다고 가정해도 좋다.
출력
각 테스트케이스마다 두 줄을 출력한다. 첫 줄에는 LIS 의 길이 L 을 출력하고, 그 다음 줄에 L 개의 정수로 K번째 LIS 를 출력한다.
입력 예제
3
9 2
1 9 7 4 2 6 3 11 10
8 4
2 1 4 3 6 5 8 7
8 2
5 6 7 8 1 2 3 4
출력 예제
4
1 2 3 11
4
1 3 6 8
4
5 6 7 8
전체 코드는 다음과 같습니다.
//
// KLIS.cpp
// AALGGO
//
// Created by inhyeok on 2021/10/10.
//
#include <iostream>
#include <fstream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
ifstream fin("KLIS.txt");
const int MAX = 2000000000 + 1;
int n; // 수열에 포함된 원소의 수;
int k; // k 번째
int cacheLen[501], cacheCnt[501], S[500];
int LIS(int start){
int &ret = cacheLen[start+1];
if(ret != -1) return ret;
ret = 1;
for(int next = start + 1; next < n; ++next){
if(start == -1 ||S[start] < S[next])
ret = max(ret, LIS(next) + 1);
}
return ret;
}
int count(int start){
//기저 사례 : 길이가 1인 경우
if(LIS(start) == 1) return 1;
int &ret = cacheCnt[start+1];
if(ret != -1) return ret;
ret = 0;
for(int next = start + 1; next < n; next++)
if((start == -1 || S[start] < S[next]) && LIS(start) == LIS(next) + 1 )
ret = min<long long>(MAX, (long long)ret + count(next));
return ret;
}
void reconstruct(int start, int skip, vector<int>& result){
// 1. S[start]는 항상 LIS에 포함된다.
if(start!=-1) result.push_back(S[start]);
// 2. 뒤에 올 수 있는 숫자들과 위치의 목록을 만든다. pair사용
vector<pair<int,int>> followers;
for(int next = start +1; next<n; next++){
if((start == -1 || S[start] < S[next]) && LIS(start) == LIS(next) + 1 )
followers.push_back(make_pair(S[next],next));
}
sort(followers.begin(), followers.end());
// 3. k번째 LIS의 다음 숫자를 찾는다.
for(int i=0; i< followers.size(); i++){
// 이 숫자를 뒤에 이어서 만들 수 있는 LIS의 개수를 본다.
int index = followers[i].second;
int cnt = count(index);
if( cnt <= skip) skip -= cnt;
else{
//다음 숫자는 S[index] 임을 알았다.
//4. 재귀호출을 통해
reconstruct(index, skip, result);
break;
}
}
}
int main(int argc, const char * argv[]) {
int test_case;
fin >> test_case;
for(int i = 0; i < test_case; i++){
fin >> n >> k;
memset(cacheLen, -1, sizeof(cacheLen));
memset(cacheCnt, -1, sizeof(cacheCnt));
for( int j =0; j< n; j++){
fin >> S[j];
}
cout << LIS(-1) -1 << endl;
vector<int> result;
reconstruct(-1,k-1,result);
for(int j=0; j<result.size(); j++){
cout << result[j] << " ";
}
cout << endl;
}
return 0;
}
Comments