Judge

Programmers - 보석 쇼핑

깡구_ 2022. 10. 13. 22:33

title: Programmers - 보석 쇼핑
date: 2022-10-13
tags:

  • Algorithm




https://school.programmers.co.kr/learn/courses/30/lessons/67258


문제 요약

진열대에 있는 보석의 특정 구간을 구매하여 모든 종류의 보석을 구매하려고 한다.
가장 적게 구매할 수 있는 구간을 찾아 시작 번호와 끝 번호만 vector에 넣어 return한다.


문제 풀이

최대 100K이며, 보석의 종류의 개수 제한이 없다.
O(n^2)의 경우 최적화를 해야 하는데, 카카오의 문제이기에 O(n) 혹은 O(nlogn)을 의도한 문제라고 판단하였다.
보석을 종류별로 분류한 후, 이 분류한 것들을 순서대로 확인하는 방식으로 문제를 해결하였다.


각 보석을 종류별로 분류하며, 진열대 번호를 저장한다.
앞에서부터 조회하였기에 각 종류별 vector는 오름차순으로 정렬이 되어 있다.
각 vector의 첫 원소들을 종합하여 max, min 값이 해당 보석들을 구매할 때의 최소 구간이다.
이 방식만 이용할 경우 효율성 테스트 13, 15번에서 TLE가 발생하였다.
여기에 priority_queue를 도입하여 함수 호출 1회만으로 정답을 구할 수 있도록 변경하였다.
가장 앞에 있는 보석을 제거한 후, 해당 보석이 더 이상 없다면 이미 최적 구간을 구했기에 바로 return한다.
보석이 남아있다면 다시 priority_queue에 넣은 후, max 값을 갱신한 후 최적 구간인지 비교한다.


끝나고 다른 유저의 풀이를 확인해보니 map을 이용하여 간단하게 해결하였다.
투포인터 방식이라면 이러한 복잡한 알고리즘 없이 빠르게 해결할 수 있는 문제였다.
카카오 문제이기에 이를 실제로 겪을 수 있는 문제에 적용해보려고 하였으나, 마땅히 떠오르는 문제가 생각나지 않는다.


프로그램

#include <vector>
#include <string>
#include <unordered_map>
#include <utility>
#include <algorithm>
#include <queue>

using std :: vector ;
using std :: string ;
using std :: unordered_map ;
using std :: pair ;
using std :: max ;
using std :: priority_queue ;

struct gemInfo
{
    int iGemIndex ;
    int iStandIndex ;
} ;

struct comp
{
    bool operator () ( gemInfo & g1 , gemInfo & g2 )
    {
        return g1.iStandIndex > g2.iStandIndex ;
    }
} ;

pair < int , int > pGetFrontRange ( vector < vector < int > > & vGemPosition )
{
    int iGemSize = vGemPosition.size () ;
    int iMax = -1 ;
    priority_queue < gemInfo , vector < gemInfo > , comp > pqGem ;
    pair < int , int > pReturn ;



    for ( int i = 0 ; i < iGemSize ; ++i )
    {
        int iTemp = vGemPosition [ i ] [ 0 ] ;

        iMax = max ( iMax , iTemp ) ;
        pqGem.push ( { i , iTemp } ) ;
    }

    pReturn.first = pqGem.top ().iStandIndex ;
    pReturn.second = iMax ;

    while ( true )
    {
        // Erase old gem
        gemInfo currentGem = pqGem.top () ;
        pqGem.pop () ;
        vGemPosition [ currentGem.iGemIndex ].erase ( vGemPosition [ currentGem.iGemIndex ].begin () ) ;


        if ( vGemPosition [ currentGem.iGemIndex ].empty () )
            break ;

        pqGem.push ( { currentGem.iGemIndex , vGemPosition [ currentGem.iGemIndex ] [ 0 ] } ) ;


        // Check if better range exist
        iMax = max ( vGemPosition [ currentGem.iGemIndex ] [ 0 ] , iMax ) ;

        if ( iMax - pqGem.top ().iStandIndex < pReturn.second - pReturn.first )
        {
            pReturn.first = pqGem.top ().iStandIndex ;
            pReturn.second = iMax ;
        }
    }


    return pReturn ;
}

vector < int > solution ( vector < string > gems )
{
    int iGemSize = gems.size () ;
    int iGemCount = 0 ;
    unordered_map < string , int > umStrToIndex ;
    vector < vector < int > > vGemPosition ;



    for ( int i = 0 ; i < iGemSize ; ++i )
    {
        int iTemp = umStrToIndex [ gems [ i ] ] ;
        if ( 0 == umStrToIndex [ gems [ i ] ] )
        {
            iTemp = ++ iGemCount ;
            umStrToIndex [ gems [ i ] ] = iTemp ;

            vector < int > vTemp ;
            vGemPosition.emplace_back ( vTemp ) ;
        }
        vGemPosition [ iTemp - 1 ].emplace_back ( i + 1 ) ;
    }

    pair < int , int > pAnswer ;
    pAnswer = pGetFrontRange ( vGemPosition ) ;


    return { pAnswer.first , pAnswer.second } ;
}