본문 바로가기
공부/STL

Hash Map

by 화릿불 2024. 6. 16.

 

Hash Map

C++ 에서는 unordered_map 컨테이너로 제공되는 것으로 알고 있다.

 

나름 map, unordered_map 을 이용해서 문제를 잘 푼다고 생각했는데

코드트리에서 실력을 진단하던 중 다음 문제에 가로막혀 버렸다.

 

두 수의 합

 

코드트리 | 코딩테스트 준비를 위한 알고리즘 정석

국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.

www.codetree.ai

 

 

중복 가능한 수의 집합에서 두 개의 수를 조합했을 때

특정 수를 만들 수 있는 조합의 수를 구하는 문제이다.

 

문제를 보자마자 map을 통해서 숫자의 수를 세고

차이 값을 기준으로 답을 구하면 될 거라고 생각했다.

 

#include <iostream>
#include <map>

using namespace std;

int n, k;
int ans = 0;
int arr[100001];
map<int, int> m;

int main()
{
    cin >> n >> k;

    for(int i=0;i<n;i++)
    {
    	int num;
        cin >> num;
        
        m[num]++;
    }
    
    for(auto item : m)
   	{
    	// 현재 수와 조합해서 k 를 만들 숫자가 있는가 ?
    	if (m.count(k - item.fisrt) != 0)
        {
        	// 만약 현재 수 * 2 = k 라면
        	if (k - item.first == item.first)
            {
            	ans++;
                m[item.first] -= 2;
            }
            // 그 외의 경우
            else
            {
            	ans++;
                m[item.first] -= 1;
                m[k - item.first] -= 1;
            }
        }
    }

    cout << ans;

    return 0;
}

 

완벽하다고 생각했는데 문제가 발생했다.

 

우선 count 의 경우 map 컨테이너에 내가 찾고자 하는 값이 있는지 없는지 판별해준다.

 

즉, 내가 이미 다 사용해서 k 의 키 값에 0 val 이 들어가 있더라도

count는 있다고 판별할 것이다.

m.count(k) = 1 -> 있다

m.count(k) = 0 -> 없다

 

이게 내가 착각했던 가장 큰 문제였다.

 

그래서 이번엔 val 값을 기준으로 답을 찾고자 했다.

 

int main()
{
    for(auto item : m)
   	{
    	// 현재 수와 조합해서 k 를 만들 숫자가 있는가 ?
    	if (m[k - item.first] != 0)
        {
        	// 만약 현재 수 * 2 = k 라면
        	if (k - item.first == item.first)
            {
            	ans += m[item.first] / 2;
                m[item.first] %= 2;
            }
            // 그 외의 경우
            else
            {
                int mini = min(m[item.first], m[k-item.first]);
            	ans += mini;
                m[item.first] -= mini;
                m[k - item.first] -= mini;
            }
        }
    }

    cout << ans;

    return 0;
}

 

한번 살펴본 item 의 경우 다시 돌아와서 판별할 수 없기 때문에

한번에 처리해주고자 하였다.

 

하지만, 이번에도 틀렸다.

코드트리는 참 친절하게도 어떤 테케에서 틀렸는지 알려준다.

 

 

이 문제는 서로 다른 자리 위치에 있는 두 수를 뽑아 합쳤을 때 k 가 되는지 판별해야 하는 문제다.

그러므로 (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3) ... 무수히 많은 결과가 나오지만

내가 짠 코드는 단 2개의 케이스만 발견할 뿐이었다.

 

10만개의 숫자에 대해서 직접 조합을 모두 구해보는 것은

10만 x 10만으로 시간복잡도 상 불가능한 방법이었고 1시간 반정도 생각했지만

더이상 방법을 생각해낼 수가 없어서 문제의 해설을 보게 되었다.

 

#include <iostream>
#include <unordered_map>

using namespace std;

int n, k;
int ans = 0;
int arr[100001];
unordered_map<int, int> m;

int main()
{
    cin >> n >> k;

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

    for(int i=0;i<n;i++)
    {
        // k = arr[i] + x 이므로 x 가 있는지 체크하고 더해 줌
        ans += m[k-arr[i]];
		
        // k = arr[i] + x 는 k = arr[i] + arr[j] 와 같으므로 j 일 때 i 가 있음을 알려주어야 함
        m[arr[i]]++;
    }

    cout << ans;

    return 0;
}

 

나는 처음부터 map 에 넣어서 풀려고 생각했는데

다시 생각해보니 중복된 원소가 서로 다른 위치에 주어질 수 있기 때문에

무작정 map을 사용한 판단 자체가 틀렸던 것이다.

 

하다못해 unordered_map 을 사용했으면 더 나았을까 ?

 

위 풀이는 원소의 순서는 배열로 관리해주고

k 에 도달하기 위한 조합 값을 map 으로 관리하는 방법인데

 

정말 생각지도 못했다.

아직도 멀었다 ...

'공부 > STL' 카테고리의 다른 글

컨테이너  (0) 2023.10.23
Compare Sort 입맛대로 정렬하기  (0) 2023.10.13