문제 풀이

 

카드 덱 n개가 있을 때, 카드 두 개를 뽑아서 두 개의 카드를 더한 결과를 뽑은 두 개의 카드에 반영한 후에 다시 카드 덱이 넣는 것을 m번 반복한다. 이때 카드 덱의 총합이 최소가 되는 것을 찾아달라는 문제다.

 

어떻게 하면 카드 덱의 총합을 최소로 만들 수 있을까?

 

카드를 뽑을 때 카드의 숫자가 작은 것을 우선적으로 뽑아서 합치면 된다.

만약에 큰 수 2개를 뽑아서 합치면 카드 덱의 총합은 그만큼 더 커지기 때문이다.

 

n개의 데이터에서 빠르게 작은 숫자를 찾는 방법은 우선순위 큐(최소 힙)이면 O(logN)만에 뽑을 수 있다.

그런데 주의할 점은 한 카드의 숫자가 최대 100만이라 int로 선언할 시 오버플로우가 발생하므로 자료형 주의가 필요하다.

 

소스 코드

 

#include <iostream>
#include <queue>

using namespace std;

typedef long long ll;

int n, m;
priority_queue<ll, vector<ll>, greater<ll> > pq;
int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);

	cin >> n >> m;
	for (int i = 0; i < n; i++)
	{
		ll x;
		cin >> x;
		pq.push(x);
	}

	while (m--)
	{
		ll x = pq.top(); pq.pop();
		ll y = pq.top(); pq.pop();
		
		pq.push(x + y);
		pq.push(x + y);
	}

	ll sum = 0;
	while (!pq.empty())
	{
		sum += pq.top();
		pq.pop();
	}

	cout << sum << endl;

	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 5670] 휴대폰 자판  (0) 2021.10.19
[백준 4358] 생태학  (0) 2021.10.19
[백준 14235] 크리스마스 선물  (0) 2021.10.13
[백준 1417] 국회위원 선거  (0) 2021.10.13
[백준 2075] N번째 큰 수  (0) 2021.10.13
문제 풀이

 

산타 할아버지는 아이들에게 선물을 줄 때, 가장 가치가 큰 선물을 준다.

선물 보따리에 머가 들었는지 모르겠지만 꺼냈을 때 그 가치가 가장 큰 것을 뽑으려면 최대 힙 구조로 구현하면 된다.

따라서 우선순위 큐를 최대 힙으로 구성해서 뽑으면 된다.

 

소스 코드

 

#include <iostream>
#include <queue>

using namespace std;

int n, a, x;
priority_queue<int> pq;
int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);

	cin >> n;
	while (n--)
	{
		cin >> a;
		if (a == 0)
		{
			if (pq.empty())
				cout << "-1\n";
			else
			{
				cout << pq.top() << '\n';
				pq.pop();
			}
		}
		else
		{
			for (int i = 0; i < a; i++)
			{
				cin >> x;
				pq.push(x);
			}
		}
	}

	return 0;
}

 

'Algorithm' 카테고리의 다른 글

[백준 4358] 생태학  (0) 2021.10.19
[백준 15903] 카드 합체 놀이  (0) 2021.10.13
[백준 1417] 국회위원 선거  (0) 2021.10.13
[백준 2075] N번째 큰 수  (0) 2021.10.13
[백준 16926] 배열 돌리기1  (0) 2021.10.12
문제 풀이

 

입력 범위가 최대 1000이라 다양한 방법이 있을 수 있다.

 

다솜이가 국회위원으로 뽑히려면 최소한 모든 투표보다는 +1 커야 한다.

3 3 3 3 3 이라면 다솜이는 최소한으로 1명을 꼬셔야 한다는 것이다.

 

3 3 4 7 5 3 이라면?

제일 많은 득표수를 가진 7에게서 3번 뺏으면? 다솜이가 당선된다.

하지만 다른 방법으로도 당선될 수도 있다.

 

어떻게 하면 최소한으로 뽑을 수 있을까?

제일 많은 득표수를 가진 사람부터 빼앗으면 된다.

 

[3 3 4 7 5 3] 를 통해서 확인해보자.

 

다솜이가 3개의 득표 수고, 나머지 국회위원들 3 4 7 5 3 중에서 가장 많은 득표수를 가진 사람은 7표이므로

7 -> 6표로 깎아주고, 다솜이는 3 -> 4표로 증가시켜준다.

 

다솜이가 4개의 득표수고, 나머지 국회위원들 3 4 6 5 3 중에서 가장 많은 득표수를 가진 사람은 6표이므로

6 -> 5표로 깎아주고, 다솜이는 4 -> 5표로 증가시켜준다.

 

다솜이가 5개의 득표수고, 나머지 국회위원들 3 4 5 5 3 중에서 가장 많은 득표수를 가진 사람은 5표이므로

5 -> 6표로 깎아주고, 다솜이는 5 -> 6표로 증가시켜준다.

 

이렇게 하면 3개의 표만 뺏어오면 다솜이가 당선되게 된다.

 

가장 많은 득표수를 빠르게 구할 수 있는 연산은 다양하지만, 우선순위 큐(최대 힙) 구조를 이용하면 쉽게 해결할 수 있다.

 

소스 코드

 

#include <iostream>
#include <queue>

using namespace std;

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);

	int n, dasom;
	cin >> n;
	priority_queue<int> pq;
	for (int i = 0; i < n; i++)
	{
		int x;
		cin >> x;
		if (i == 0)
			dasom = x;
		else
			pq.push(x);
	}

	int ans = 0;
	while (!pq.empty())
	{
		int now = pq.top();
		pq.pop();

		if (dasom <= now)
		{
			dasom++;
			now--;
			ans++;
			pq.push(now);
		}
		else
			break;
	}

	cout << ans << endl;
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 15903] 카드 합체 놀이  (0) 2021.10.13
[백준 14235] 크리스마스 선물  (0) 2021.10.13
[백준 2075] N번째 큰 수  (0) 2021.10.13
[백준 16926] 배열 돌리기1  (0) 2021.10.12
[백준 17298] 오큰수  (0) 2021.10.11
문제 풀이

단순하게 완전탐색법으로 접근하면 1초 안에 해결 가능한 범위이지만,

문제에서 보면 메모리 제한이 12MB로 다른 문제들에 비해서 무척이나 작은 공간이다.

 

12MB => 12 x 1000 x 1000

만약에 2차원 배열을 사용한다고 하면?

int(4byte) x 1500 x 1500 => 9,000,000 충분하다고 생각하지만 여기에 헤더 파일 등등 포함되면 빡빡하게 된다.

따라서 모든 자료를 저장할 필요 없이 N번째 큰 수를 찾아야 한다.

 

즉, 일부를 알기 위해서 전부를 저장할 필요가 없다.

 

우선순위 큐를 이용하는 방법이다.

다만 이때 우선순위 큐도 전체 자료를 저장하게 된다면, 마찬가지로 메모리 초과가 발생하게 된다. 이유는 위와 같다.

따라서 우선순위 큐를 사용하지만 일정 범위 이상으로 넘쳐흐르게 된다면 그중에서 필요 없는 값을 빼고 새로 들어온 값을 넣으면서 일부만 저장하는 방법을 취한다.

 

N = 3 이라고 할 때

입력값 : 3 5 6 7 8 이라고 하면,

 

처음 3 5 6 은 우선순위 큐에 저장하게 된다.

 

다음으로 들어온 숫자 7은 우선순위 큐(최소 힙)의 루트는 3이므로 3을 버리고 7을 넣는다.

왜? 3이라는 숫자는 필요 없는 값이기 때문에, 그렇지만 7이라는 숫자는 의미 있는 숫자이기 때문이다.

 

숫자 8이 들어오면 우선순위 큐의 루트는 5이므로 5를 버리고 8을 넣는다.

 

최종적으로는 우선순위 큐에는 6 7 8 이 존재하게 된다.

여기서 우리가 구하고자 하는 N = 3번째로 큰 수는 우선순위 큐의 루트인 6이 정답이 된다.

 

소스 코드

 

#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

int n, x;
priority_queue<int, vector<int>, greater<int> > pq;

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);

	cin >> n;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			cin >> x;
			pq.push(x);
			if (pq.size() > n)
				pq.pop();
		}
	}

	cout << pq.top() << endl;
	return 0;
}

 

'Algorithm' 카테고리의 다른 글

[백준 14235] 크리스마스 선물  (0) 2021.10.13
[백준 1417] 국회위원 선거  (0) 2021.10.13
[백준 16926] 배열 돌리기1  (0) 2021.10.12
[백준 17298] 오큰수  (0) 2021.10.11
[백준 14425] 문자열 집합  (0) 2021.10.11
문제 풀이

 

반시계 방향으로 배열을 돌리면 된다.

단순하게 생각해서 구현 문제이며, 어떻게 구현하냐에 따라 풀이가 제각각일 것 같다.

 

반시계 방향으로 배열을 돌린다고 생각할 때,

가장 바깥쪽부터 반시계 방향으로 회전하는 식으로 구현했다.

 

가장 바깥쪽 반시계 돌리기,

그다음 바깥쪽 반시계 돌리기,

언제까지? 좌표가 엇갈릴 때까지 돌려주면 된다.

 

바깥쪽의 구분은

왼쪽 대각선의 시작점 (sx, sy) 와 오른쪽 대각선 끝점 (ex, ey)를 기준으로 잡아서 시작과 끝을 구분했다.

엇갈린다는 것은 sx >= ex || sy >= ey 를 의미한다.

 

배열을 돌릴 때는

swap 개념을 이용해서 기존의 값을 임시 저장소 temp에 저장하고,

다 돌리고 나서 temp의 값을 넣어주면 된다.

이때 원소의 이동은 한 칸씩 옮겨주면 된다.

 

소스 코드

 

#include <iostream>

using namespace std;

const int MAX = 300 + 5;

int N, M, R;
int a[MAX][MAX];

void rotate()
{
    int sx = 1, sy = 1;
    int ex = N, ey = M;
    while(sx < ex && sy < ey)
    {
        int temp = a[sx][sy];
        
        //top
        for(int i = sy + 1; i <= ey; i++)
        {
            a[sx][i-1] = a[sx][i];
        }
        
        //right
        for(int i = sx + 1; i <= ex; i++)
        {
            a[i-1][ey] = a[i][ey];
        }
        
        //bottom
        for(int i = ey; i >= sy + 1; i--)
        {
            a[ex][i] = a[ex][i-1];
        }
        
        //left
        for(int i = ex; i >= sx + 1; i--)
        {
            a[i][sy] = a[i-1][sy];
        }
        
        a[sx+1][sy] = temp;
        
        sx++;
        sy++;
        ex--;
        ey--;
    }
}

void printMatrix()
{
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= M; j++)
        {
            cout << a[i][j] << ' ';
        }
        cout << '\n';
    }
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> N >> M >> R;
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= M; j++)
        {
            cin >> a[i][j];
        }
    }
    
    for(int i = 0; i < R; i++)
        rotate();
    
    printMatrix();
    
    return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 1417] 국회위원 선거  (0) 2021.10.13
[백준 2075] N번째 큰 수  (0) 2021.10.13
[백준 17298] 오큰수  (0) 2021.10.11
[백준 14425] 문자열 집합  (0) 2021.10.11
[백준 1753] 최단경로  (0) 2021.10.11
위상 정렬이란?

 

사전 작업, 선행 작업 등 순서가 정해진 작업을 차례대로 수행해야 할 때, 그 순서를 나열한 것.

사이클이 존재하지 않는 방향 그래프의 모든 노드를 순서에 거스르지 않도록 나열한 것.

 

'스타크래프트' 게임을 생각하면 쉽게 이해할 수 있다.

'서플', '배럭'을 짓기 위해서는 선행 작업으로 '커맨트 센터'를 먼저 만들어야 한다.

이 순서를 어기게 되면, 영원히 '서플'과 '배럭'을 건설할 수 없게 된다.

 

이렇듯, 선행이 필수인 것부터 나열해서 정렬하는 것이 위상 정렬이다.

 

위상 정렬의 동작 과정

 

  1. 진입 차수가 0인 노드 먼저 큐에 넣는다.
  2. 큐가 빌 때까지 반복한다.
    1. 큐에서 원소를 꺼내고, 그 노드에서 출발하는 간선들을 모두 제거한다.
    2. 이때 새롭게 진입 차수가 0인 노드들은 큐에 넣어준다.

큐에서 빠져 나오는 순서가 바로 구하려고 하는 위상 정렬이다.

진입 차수(indegree)는 해당 노드로 들어오는 간선의 수를 의미한다.

 

위상 정렬의 시간 복잡도

 

V : 노드의 수

E : 간선의 수

시간 복잡도 : 인접 리스트로 구현하면 O(V + E)지만, 인접 행렬로 구현하면 O(V^2)

 

차례대로 모든 노드들을 확인하면서 해당 노드에서 출발하는 간선들을 차례대로 제거하므로

결과적으로는 모든 노드와 모든 간선을 확인하기 때문이다.

 

위상 정렬 구현

 

위상 정렬에 필요한 것은 Queue의 자료구조

입력 범위에 따라 인접 행렬, 인접 리스트로 그래프를 구성하고 해결한다.

#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>

using namespace std;

const int MAX = 32000 + 1;

int V, E;
int indegree[MAX];
vector<int> adj[MAX];

void topologySort()
{
    vector<int> ret;
    queue<int> q;
    
    //처음 시작할 때는 진입차수가 0인 노드를 큐에 넣어준다.
    for(int i = 1; i <= V; i++)
    {
        if(indegree[i] == 0)
            q.push(i);
    }
    
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        
        //큐에서 빠져 나온 순서가 정렬 순서
        ret.emplace_back(now);
        
        //해당 원소와 연결된 노드들의 진입차수에서 1빼기
        for(int i = 0; i < adj[now].size(); i++)
        {
            int next = adj[now][i];
            indegree[next]--;
            //새롭게 진입차수가 0이 되는 노드는 큐에 넣어주자.
            if(indegree[next] == 0)
                q.push(next);
        }
    }
    
    for(const auto& i : ret)
    {
        cout << i << ' ';
    }
    cout << '\n';
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> V >> E;
    for(int i = 0; i < E; i++)
    {
        int a, b;
        cin >> a >> b;
        adj[a].push_back(b);
        indegree[b]++;
    }
    
    topologySort();
    
    return 0;
}
한 걸음 더 나아가서

 

위상 정렬 도중에 큐가 비게 된다는 것은 사이클이 존재한다는 것을 의미한다.

운영체제 시간에 배운 순환 대기 형태를 생각해보면 이해하기 쉽다.

 

이렇게 순환 대기 형태를 이루게 되면(사이클을 이루게 되면) 순환 대기 형태를 띠는 노드들은 진입 차수가 0이 될 수 없으므로,

위상 정렬 큐에 들어가지 못하고 중간에 끝나버리게 된다.

 

앞서 말했듯이 위상 정렬은 모든 노드들과 모든 간선을 확인해야 하는데,

모든 노드를 탐색하기도 전에 큐가 empty 형태가 되어 끝나버리게 되는 것이다.

 

즉, 이러한 사이클 구조를 발견하기 위해서는 모든 노드를 탐색하기 전에 정렬이 끝나버리는 것을 체크하면 해결할 수 있다.

void topologySort()
{
	vector<int> ret;
	priority_queue<int, vector<int>, greater<int> > q;

	for (int i = 1; i <= v; i++)
	{
		if (indegree[i] == 0) q.push(i);
	}

	for (int i = 1; i <= v; i++)
	{
		// 위상 정렬 도중에 큐가 비게 된다면 사이클이 존재한다는 의미
		if (q.empty())
		{
			cout << -1 << '\n';
			return;
		}

		int now = q.top(); q.pop();
		ret.push_back(now);

		for (int i = 0; i < graph[now].size(); i++)
		{
			int next = graph[now][i];
			indegree[next]--;
			if (indegree[next] == 0) q.push(next);
		}
	}

	for (int n : ret) cout << n << '\n';
}

 

위상 정렬의 답은 1개가 아니다?

 

때로는 위상 정렬의 답은 여러개가 될 수 있다.

진입 차수가 0이 되는 노드가 2개 이상이 큐에 들어가는 경우가 발생하면 여러 가지 답이 나올 수 있다.

'Algorithm > Algorithm Concept' 카테고리의 다른 글

트라이 (Trie) 자료구조  (0) 2021.10.18
행렬의 다양한 연산들  (0) 2021.10.11
플로이드 워셜 (Floyd Warshall)  (0) 2021.09.03
투 포인터  (0) 2021.08.25
다익스트라 알고리즘  (0) 2021.08.24
문제 풀이

 

오큰수를 다시 정리하면 다음과 같다.

나보다 크며, 오른쪽 중에서는 가장 작은 수여야 한다.

[3 5 2 7]에서 내가 5라고 가정하면, 오큰수는 7이다.

 

단순하게 완전 탐색으로 돌리게 되면 O(N^2)이 걸리게 된다.

N이 100만이기 때문에 시간 초과가 발생하므로 조금 더 빠르게 오큰수를 찾는 방법이 필요하다.

 

이런 문제는 스택 자료구조를 사용하면 O(N)으로 해결할 수 있게 된다.

스택은 아래와 같은 방법으로 동작한다.

1) 스택이 비어있으면 -1 (오큰수가 없으므로) 처리하고, 스택에 push 한다. 푸시하는 이유는 다음의 오큰수에 해당될 수 있으므로

2) 스택이 비어있지 않으면,

입력된 arr[i] < stack의 top이면? stack의 top이 해당 오큰수를 뜻하고,

>= 이면 <가 나올때까지 스택에서 계속해서 오큰수를 pop 하면서 찾는다.

만약에 찾게 된다면 해당 수가 오큰수이며, 입력된 arr[i]를 스택에 push 한다.

못 찾게 된다면 해당 수에 오큰수가 존재하지 않으므로 -1이다.

 

입력된 배열의 역순으로 하나씩 확인하면서 스택을 응용하면 된다.

 

문제에서 주어진 예시로 다시 확인해보자.

입력된 수 스택 자료구조 오큰수 배열
7 empty  
  7 [0, 0, 0, -1]
2 7  
  7 2 [0, 0, 7, -1]
5 7 2  
  7 5 [0, 7, 7, -1]
3 7 5  
  7 5 3 [5, 7, 7, -1]

입력된 수 : 7

현재 스택이 비어있기 때문에 오큰수는 -1이며, 스택에 7을 삽입하면 된다.

 

입력된 수 : 2

현재 스택의 top(7)이 2보다 크므로 2의 오큰수는 7이며, 스택에 2를 삽입한다.

 

입력된 수 : 5

현재 스택의 top(2)보다 5가 더 크므로 스택에서 하나를 뺀다.

현재 스택의 top(7)이 5보다 크므로 5의 오큰수는 7이며, 스택에 5를 삽입한다.

 

입력된 수 : 3

현재 스택의 top(5)이 3보다 크므로 3의 오큰수는 5이며, 스택에 3을 삽입한다.

 

소스 코드

 

#include <iostream>
#include <stack>

using namespace std;

int N;
int arr[1000001];
int ans[1000001];

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> N;
    for(int i = 0; i < N; i++)
        cin >> arr[i];
    
    stack<int> st;
    for(int i = N - 1; i >= 0; i--)
    {
        while(!st.empty() && st.top() <= arr[i])
            st.pop();
        
        if(!st.empty())
        {
            ans[i] = st.top();
        }
        else
            ans[i] = -1;
        
        st.push(arr[i]);
    }
    
    for(int i = 0; i < N; i++)
        cout << ans[i] << ' ';
    
    return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 2075] N번째 큰 수  (0) 2021.10.13
[백준 16926] 배열 돌리기1  (0) 2021.10.12
[백준 14425] 문자열 집합  (0) 2021.10.11
[백준 1753] 최단경로  (0) 2021.10.11
[HackerRank] 2D Array - DS  (0) 2021.09.09
문제 풀이

 

주어진 문자열 집합 S에서 입력되는 M개의 문자열이 같은 게 몇 개인지를 찾는 문제다.

단순히 생각해보면 전체 집합 S에서 전체 M개의 문자열을 2중 탐색으로 반복해서 문자열 비교까지 하게 된다면

시간 복잡도가 O(10,000 x 10,000 x 500) = 2초를 훌쩍 넘는다.

 

여기서 줄일 수 있는 방법은 문자열 탐색 속도를 줄이는 것이다.

균형 이진 탐색 트리를 사용하는 set이나 map을 이용하게 되면 O(logN) 만에 탐색 속도를 줄일 수 있다.

 

소스 코드

 

#include <iostream>
#include <map>
#include <string>
#include <vector>

using namespace std;

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    int N, M;
    cin >> N >> M;
    map<string, bool> m;
    for(int i = 0; i < N; i++)
    {
        string str;
        cin >> str;
        m[str] = true;
    }
    
    int cnt = 0;
    for(int i = 0; i < M; i++)
    {
        string str;
        cin >> str;
        if(m[str] == true)
        {
            cnt++;
        }
    }
    
    cout << cnt << endl;
    return 0;
}
트라이 이용

 

신기하게도 이 문제는 트라이 자료구조를 이용해서 풀 수 있다.

다만 시간 복잡도가 map은 O(문자열의 개수 10000 x 탐색 시간(log10000))이지만

트라이는 O(문자열의 개수 10000 x 문자열의 길이 500)이므로 여기서는 트라이가 더 느리다.

 

트라이가 항상 빠르다고 생각하면 큰 오산이 되는 문제다.

 

트라이에 문자열 집합 S를 넣어서 완성시키고, 쿼리마다 find 함수를 통해서 등록되어 있으면 카운팅 해주면 된다.

소스 코드(트라이)

 

#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Trie
{
    Trie* ch[26];
    bool end;
    
    Trie() : end(false)
    {
        for(int i = 0; i < 26; i++)
            ch[i] = nullptr;
    }
    
    ~Trie()
    {
        for(int i = 0; i < 26; i++)
            if(ch[i]) delete ch[i];
    }
    
    void insert(const char* str)
    {
        if(!*str)
        {
            end = true;
            return;
        }
        
        int cur = *str - 'a';
        if(!ch[cur]) ch[cur] = new Trie();
        ch[cur]->insert(str + 1);
    }
    
    bool find(const char* str)
    {
        if(!*str)
        {
            if(end) return true;
            return false;
        }
        int cur = *str - 'a';
        if(!ch[cur]) return false;
        return ch[cur]->find(str + 1);
    }
};

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    int n, m;
    cin >> n >> m;
    Trie* root = new Trie();
    string str;
    for(int i = 0; i < n; i++)
    {
        cin >> str;
        
        root->insert(str.c_str());
    }
    
    int cnt = 0;
    for(int i = 0; i < m; i++)
    {
        cin >> str;
        
        if(root->find(str.c_str()))
        {
            cnt++;
        }
    }
    
    cout << cnt << endl;
    delete root;
    return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 16926] 배열 돌리기1  (0) 2021.10.12
[백준 17298] 오큰수  (0) 2021.10.11
[백준 1753] 최단경로  (0) 2021.10.11
[HackerRank] 2D Array - DS  (0) 2021.09.09
[프로그래머스] 방문 길이 (C++)  (0) 2021.06.04
문제 풀이

 

방향 그래프이면서 하나의 시작점에서 다른 여러 개의 모든 지점의 최단 경로를 구하는 방법은 다익스트라 알고리즘을 구현하면 해결할 수 있다.

 

소스 코드 (우선순위 큐를 이용한 구현)

 

#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

const int INF = 987654321;

int V, E, startNode;
vector<pair<int, int> > edges[20001];
int dist[20001];

void dijkstra()
{
    dist[startNode] = 0;
    priority_queue<pair<int, int> > pq;
    pq.push({0, startNode});
    while(!pq.empty())
    {
        int distance = -pq.top().first;
        int curNode = pq.top().second;
        pq.pop();
        
        if(dist[curNode] < distance)
            continue;
        
        for(int i = 0; i < edges[curNode].size(); i++)
        {
            int nextNode = edges[curNode][i].first;
            int nextDistance = edges[curNode][i].second + distance;
            if(nextDistance < dist[nextNode])
            {
                dist[nextNode] = nextDistance;
                pq.push({-nextDistance, nextNode});
            }
        }
    }
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> V >> E >> startNode;
    fill(dist, dist + 20001, INF);
    for(int i = 0; i < E; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        
        edges[u].emplace_back(v,w);
    }
    
    dijkstra();
    
    for(int i = 1; i <= V; i++)
    {
        if(dist[i] == INF)
            cout << "INF\n";
        else
            cout << dist[i] << '\n';
    }
    
    return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 17298] 오큰수  (0) 2021.10.11
[백준 14425] 문자열 집합  (0) 2021.10.11
[HackerRank] 2D Array - DS  (0) 2021.09.09
[프로그래머스] 방문 길이 (C++)  (0) 2021.06.04
[프로그래머스] 영어 끝말잇기 (C++)  (0) 2021.06.04

간단하게 행렬을 표현하는 방법에서 코딩 테스트에서 자주 나오는 행렬에 대한 몇 가지 구현 방법을 공부해보자.

 

행렬의 표현

 

행렬의 표현은 자료구조에서 보통 2차원 배열로 표현하게 된다.

만약에 큐브라면 3차원 배열로 구현하면 된다.

//2 x 2 행렬
int a[2][2] = 
{
    {1, 1},
    {1, 1}
};

//3 x 3 행렬
int b[3][3] =
{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 2 x 5 행렬
int c[2][5] = 
{
    {1, 2},
    {3, 4},
    {5, 6},
    {7, 8},
    {9, 10}
};
행렬의 곱셈

 

"A의 행렬 x B의 행렬" 를 물어보는 문제가 간간히 등장한다.

이럴 때 행렬의 곱셈을 구현하는 방법을 따로 연습하지 않았더라면 꽤나 시간을 잡아먹을 수 있다!

 

핵심은 다음과 같다.

C의 행렬[i][j] = A의 행렬의 i 행 x B의 행렬의 j열

 

구현은 다음과 같이 할 수 있다.

#include <iostream>

using namespace std;

int main()
{
    int a[2][3] = {{1,2,3},{4,5,6}};
    int b[3][2] = {{7,8},{9,10},{11,12}};
    int c[2][2] = {{0,0},{0,0}};
    
    // 행렬의 곱셈 구현 : 시간복잡도 O(N^3)
    for(int i = 0; i < 2; i++) // a의 행
    {
        for(int j = 0; j < 2; j++) // b의 열
        {
            for(int k = 0; k < 3; k++) // a의 열 == b의 행
            {
                c[i][j] += a[i][k] * b[k][j];
            }
        }
    }
    
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            cout << c[i][j] << ' ';
        }
        cout << '\n';
    }
    
    return 0;
}
행렬의 회전

 

행렬의 회전하는 것도 자주 등장하는 문제 중 하나다.

시계방향인지, 반시계방향인지 생각하고 구현 방법을 배워놓으면 나중에 실수를 줄일 수 있다.

#include <iostream>
#include <vector>

using namespace std;

typedef vector<vector<int> > matrix;

// N x N 시계방향으로 90도 회전
matrix rotateRight(const matrix& a)
{
    int n = static_cast<int>(a.size());
    matrix ret(n, vector<int>(n, 0));
    
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            ret[j][n - 1 - i] = a[i][j];
        }
    }
    return ret;
}

// N x M 시계방향으로 90도 회전
matrix rotateRightOther(const matrix& a)
{
    int r = static_cast<int>(a.size());
    int c = static_cast<int>(a[0].size());
    //주의! M x N 으로 만들자.
    matrix ret(c, vector<int>(r,0));
    
    for(int i = 0; i < r; i++)
    {
        for(int j = 0; j < c; j++)
        {
            ret[j][r - 1 - i] = a[i][j];
        }
    }
    return ret;
}

//N x N 반시계방향으로 90도 회전
matrix rotateLeft(const matrix& a)
{
    int n = static_cast<int>(a.size());
    matrix ret(n, vector<int>(n,0));
    
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            ret[n - 1 - j][i] = a[i][j];
        }
    }
    return ret;
}

// N x M 반시계방향으로 90도 회전
matrix rotateLeftOther(const matrix& a)
{
    int r = static_cast<int>(a.size());
    int c = static_cast<int>(a[0].size());
    matrix ret(c, vector<int>(r,0));
    
    for(int i = 0; i < r; i++)
    {
        for(int j = 0; j < c; j++)
        {
            ret[c - 1 - j][i] = a[i][j];
        }
    }
    return ret;
}

void printMatrix(const matrix& a)
{
    int r = static_cast<int>(a.size());
    int c = static_cast<int>(a[0].size());
    for(int i = 0; i < r; i++)
    {
        for(int j = 0; j < c; j++)
        {
            cout << a[i][j] << ' ';
        }
        cout << '\n';
    }
    cout << '\n';
}

int main()
{
    matrix a(2, vector<int>(2, 0));
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
            a[i][j] = i * 2 + j;
    
    cout << "a의 회전하기 전의 모양" << endl;
    printMatrix(a);
    
    cout << "a를 시계방향으로 90도 회전" << endl;
    printMatrix(a = rotateRight(a));
    
    cout << "a를 시계방향으로 180도 회전" << endl;
    printMatrix(a = rotateRight(a));
    
    cout << "a를 시계방향으로 270도 회전" << endl;
    printMatrix(a = rotateRight(a));
    
    cout << "a를 시계방향으로 360도 회전" << endl;
    printMatrix(a = rotateRight(a));
    
    cout << "a를 반시계방향으로 90도 회전" << endl;
    printMatrix(a = rotateLeft(a));
    
    cout << "a를 반시계방향으로 180도 회전" << endl;
    printMatrix(a = rotateLeft(a));
    
    cout << "a를 반시계방향으로 270도 회전" << endl;
    printMatrix(a = rotateLeft(a));
    
    cout << "a를 반시계방향으로 360도 회전" << endl;
    printMatrix(a = rotateLeft(a));
    
    matrix b(2, vector<int>(3, 0));
    int num = 0;
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 3; j++)
            b[i][j] = num++;
    
    cout << "b의 회전하기 전의 모양" << endl;
    printMatrix(b);
    
    cout << "b를 시계방향으로 90도 회전" << endl;
    printMatrix(b = rotateRightOther(b));
    
    cout << "b를 시계방향으로 180도 회전" << endl;
    printMatrix(b = rotateRightOther(b));
    
    cout << "b를 시계방향으로 270도 회전" << endl;
    printMatrix(b = rotateRightOther(b));
    
    cout << "b를 시계방향으로 360도 회전" << endl;
    printMatrix(b = rotateRightOther(b));
    
    cout << "b를 반시계방향으로 90도 회전" << endl;
    printMatrix(b = rotateLeftOther(b));
    
    cout << "b를 반시계방향으로 180도 회전" << endl;
    printMatrix(b = rotateLeftOther(b));
    
    cout << "b를 반시계방향으로 270도 회전" << endl;
    printMatrix(b = rotateLeftOther(b));
    
    cout << "b를 반시계방향으로 360도 회전" << endl;
    printMatrix(b = rotateLeftOther(b));
    
    return 0;
}

'Algorithm > Algorithm Concept' 카테고리의 다른 글

트라이 (Trie) 자료구조  (0) 2021.10.18
위상 정렬 (TopologySort)  (0) 2021.10.12
플로이드 워셜 (Floyd Warshall)  (0) 2021.09.03
투 포인터  (0) 2021.08.25
다익스트라 알고리즘  (0) 2021.08.24

+ Recent posts