문제 풀이

 

간단하게 MST 알고리즘을 구현할 수 있는지 물어보는 문제다.

그중에서도 크루스칼 알고리즘을 이용해서 풀었다.

 

크루스칼 알고리즘은 유니온 파인드와 정렬을 이용해서 MST를 구상하게 된다.

따라서 기본적인 크루스칼 알고리즘을 구현하면 문제를 해결할 수 있다.

 

소스 코드
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX = 10001;
int V, E;
vector<pair<int, pair<int, int> > > edges;
int parent[MAX];

int findParent(int x)
{
	if (x == parent[x]) return x;
	return parent[x] = findParent(parent[x]);
}

void unionParent(int a, int b)
{
	a = findParent(a);
	b = findParent(b);
	if (a < b) parent[b] = a;
	else parent[a] = b;
}

void input()
{
	cin >> V >> E;
	for (int i = 0; i < E; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		edges.push_back({ c, {a,b} });
	}

	for (int i = 1; i <= V; i++) parent[i] = i;
}

void solve()
{
	input();

	sort(edges.begin(), edges.end());

	long long ans = 0;
	int len = edges.size();
	for (int i = 0; i < len; i++)
	{
		int cost = edges[i].first;
		int a = edges[i].second.first;
		int b = edges[i].second.second;
		if (findParent(a) != findParent(b))
		{
			unionParent(a, b);
			ans += cost;
		}
	}

	cout << ans << endl;
}

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

'Algorithm' 카테고리의 다른 글

[백준 1922] 네트워크 연결  (0) 2021.11.19
[백준 1647] 도시 분할 계획  (0) 2021.11.18
[백준 11779] 최소비용 구하기2  (0) 2021.11.15
[백준 13549] 숨바꼭질3  (0) 2021.11.14
[백준 14503] 로봇 청소기  (0) 2021.11.14
문제 풀이

 

약간만 생각하면 생각보다 쉽게 풀리는 문제다.

기존에 다익스트라를 통해서 한 정점에서 각 정점들의 최단 경로를 구했다면,

한 정점에서 시작해서 도착 정점까지의 경로들을 나열해야 한다.

 

어떻게 하면 나열할 수 있을까?

다익스트라의 원리를 보면 해결할 수 있다.

 

경로를 갱신할 때, 그때가 그 최단 경로이기 때문에 그 경로를 저장해주면 된다.

마치 'Union-Find'처럼 나의 부모가 누군지 저장해가면서, 최종적으로 역순으로 나열하면 그것이 최단 경로를 가지는 도시의 순서다.

 

출력할 때 약간 지저분할 수 있지만, 정리하면 다음과 같다.

1. 도착 지점까지의 최소 비용 = dist[도착 도시]

2. 도시의 개수 = 최단 경로를 가지는 도시의 순서의 도시를 카운트하면 된다.

3. 도시의 순서 = 도착 도시의 부모부터 시작해서 출발 도시까지 도시들을 담은 다음에 역순으로 출력하면 된다.

 

소스 코드
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;

const int INF = 987654321;
const int MAX = 1001;
int parent[MAX];
int dist[MAX];
int n, m, startCity, endCity;
vector<pair<int, int> > adj[MAX];

void dijkstra()
{
	fill(dist, dist + MAX, INF);
	for (int i = 1; i <= n; i++) parent[i] = -1;
	priority_queue<pair<int, int> > pq;
	pq.push({ 0, startCity });
	while (!pq.empty())
	{
		int cur = pq.top().second;
		int distance = -pq.top().first;
		pq.pop();

		if (dist[cur] < distance) continue;

		for (int i = 0; i < adj[cur].size(); i++)
		{
			int next = adj[cur][i].first;
			int nextDistance = adj[cur][i].second + distance;
			// 더 짧은 경로가 있다면 해당 경로로 갱신하는 타이밍,
			// 이때가 최단 경로이므로 부모를 저장한다. (추적하기 위해서)
			if (dist[next] > nextDistance)
			{
				dist[next] = nextDistance;
				parent[next] = cur;
				pq.push({ -nextDistance, next });
			}
		}
	}

	cout << dist[endCity] << '\n';
	int cnt = 0, cur = endCity;
	vector<int> v;
	v.push_back(cur);
	while (parent[cur] != startCity)
	{
		v.push_back(parent[cur]);
		cur = parent[cur];
	}
	v.push_back(startCity);
	reverse(v.begin(), v.end());

	cout << v.size() << '\n';
	for (auto i : v)
	{
		cout << i << ' ';
	}
	cout << '\n';
}

void input()
{
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		adj[a].emplace_back(b, c);
	}
	cin >> startCity >> endCity;
}

void solve()
{
	input();
	dijkstra();
}

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

	solve();
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 1647] 도시 분할 계획  (0) 2021.11.18
[백준 1197] 최소 스패닝 트리  (0) 2021.11.17
[백준 13549] 숨바꼭질3  (0) 2021.11.14
[백준 14503] 로봇 청소기  (0) 2021.11.14
[백준 9205] 맥주 마시면서 걸어가기  (0) 2021.11.13
문제 풀이

 

간단하게 BFS로 작성하고 제출하면 틀렸다고 나온다.

 

수빈이는

0초에 순간이동으로 현재 지점 x -> 2x로 움직일 수 있고,

1초에 -1 혹은 1로 움직일 수 있다.

 

그런데 BFS 라는 알고리즘모든 간선의 가중치가 동일해야 한다는 전제 조건이 필요하기 때문이다.

이 문제 같은 경우는 동일하지 않은 가중치가 존재한다.

 

이러한 문제를 해결하기 위해서는 상식적으로 현재 공간에서 순간이동, 후진, 전진 중 시간 소모가 없는 순간이동부터 하는 것이 최단 경로다. 그런데 어떤 경우에서는 순간 이동보다 후진 혹은 전진이 먼저 큐에서 나올 수 있기 때문에

큐에 넣을 때 간선의 가중치가 훨씬 적은 순간이동부터 최우선으로 넣어야하는 것만 주의하면 된다.

이런 알고리즘을 0-1 BFS라고 한다.

 

0-1 BFS를 구현하는 방법은 여러가지가 있겠지만 deque를 이용해서 간단하게 구현할 수 있다.

가중치가 낮은 순간이동의 경우는 큐의 앞쪽에 넣고

가중치가 높은 후진이나 전진의 경우는 큐의 뒤쪽에 넣어서

최단 경로를 보장할 수 있도록 구현하면 문제를 해결할 수 있다.

 

소스 코드
#include <iostream>
#include <queue>
#include <deque>
using namespace std;

const int MAX = 200002;
int N, K;
bool visit[MAX];

int bfs()
{
	deque<pair<int, int> > dq;
	dq.emplace_back(N, 0);
	visit[N] = true;
	while (!dq.empty())
	{
		int cur = dq.front().first;
		int cnt = dq.front().second;
		dq.pop_front();

		if (cur == K)
		{
			return cnt;
		}

		if (cur * 2 < MAX)
		{
			if (visit[cur * 2] == false)
			{
				visit[cur * 2] = true;
				dq.emplace_front(cur * 2, cnt);
			} 
		}

		if (cur + 1 < MAX)
		{
			if (visit[cur + 1] == false)
			{
				visit[cur + 1] = true;
				dq.emplace_back(cur + 1, cnt + 1);
			}
		}

		if (cur - 1 >= 0)
		{
			if (visit[cur - 1] == false)
			{
				visit[cur - 1] = true;
				dq.emplace_back(cur - 1, cnt + 1);
			}
		}
	}
	return -1;
}

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

	cin >> N >> K;

	cout << bfs() << endl;
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 1197] 최소 스패닝 트리  (0) 2021.11.17
[백준 11779] 최소비용 구하기2  (0) 2021.11.15
[백준 14503] 로봇 청소기  (0) 2021.11.14
[백준 9205] 맥주 마시면서 걸어가기  (0) 2021.11.13
[백준 6593] 상범 빌딩  (0) 2021.11.11
문제 풀이

 

생각보다 조건이 되게 많은 문제다.

간단하게 생각해보면 현실에서 로봇 청소기를 생각해보자.

현재 위치와 방향에서 앞에 벽이 없다면? 아마도 전진한 다음 그곳을 청소한다.

현재 위치와 방향에서 앞에 벽이 있다면? 왼쪽이나 오른쪽으로 돌아 선 다음 그곳에서 다시 벽을 체크할 것이다.

이렇게 내가 로봇 청소기라면 어떻게 판단할지 생각하면서 문제를 다시 보자.

 

이제 하나씩 조건을 보면서 생각해보자.

1) 현재 위치를 청소한다.

말 그대로 현재 위치를 청소했다고 방문처리를 하고 청소 횟수를 +1 하면 된다.

(주의할 점이 로봇이 처음 주어진 좌표도 청소해야 되므로 초기값은 1이다.)

 

2) 현재 위치에서 현재 방향을 기준으로 왼쪽 방향부터 차례대로 인접한 칸을 탐색한다.

말이 긴데 요약하면 현재 위치에서 왼쪽 방향부터 차례대로 보겠다는 뜻이다.

 

2-1) 왼쪽 방향에 아직 청소하지 않은 공간이 존재한다면, 그 방향으로 회전한 다음 한 칸을 전진하고 1번부터 진행한다.

일단 왼쪽 방향으로 틀고 한 칸 전진한 위치를 보고 판단하라는 것이다.

 

2-2) 왼쪽 방향에 청소할 공간이 없다면, 그 방향으로 회전하고 2번으로 돌아간다.

청소할 공간이 없다는 건, 이미 청소를 했거나 혹은 벽을 뜻한다.

이럴 때는 어쩔 수 없이 다시 왼쪽 방향을 살펴보자.

 

2-3) 4 방향 모두 청소가 이미 되어있거나 벽인 경우에는, 바라보는 방향을 유지한 채로 한 칸 후진을 하고 2번으로 돌아간다.

2-4) 4 방향 모두 청소가 이미 되어있거나 벽이면서, 뒤쪽 방향이 벽이라 후진도 할 수 없는 경우에는 작동을 멈춘다.

2-3 조건과 2-4 조건은 비슷해서 한 번에 해석이 가능하다.

자동차를 떠올려보자. 둘러봤는데 앞으로 전진할 수 없으면 어떻게 할까? 후진해야 된다.

그런데? 후진할 공간도 벽이면 결국 내 위치에서 후진하는 쪽도 벽이 있다는 뜻이다.

그러면 그 청소기는 어떤 행동도 하지 못하고 결국 종료.

종료할 때 내가 청소했던 횟수를 출력하기만 하면 된다.

 

의미는 알겠는데 구현을 어떻게 할까..

현재 위치에서 왼쪽, 왼쪽, 왼쪽, 왼쪽 하면 자기 자신으로 돌아온다.

즉 4번 루프를 돌려서 모든 구역을 확인해보고 만약에 청소할 공간을 찾았다면? 종료시키고 그쪽으로 다음 상태를 전달하면 된다.

 

그런데 4번을 확인해도 찾지 못했다는 건?

2-3 조건과 2-4 조건을 체크해서 2-3에 해당한다면 로봇 청소기를 후진시켜서 다음 상태로 전달하면 되고,

2-4에 해당한다면 현재까지 청소한 횟수를 출력하고 종료하면 된다.

 

소스 코드
#include <iostream>
#include <queue>
using namespace std;

const int MAX = 55;
const int dx[] = { -1,0,1,0 };
const int dy[] = { 0,1,0,-1 };

int N, M;
int board[MAX][MAX];
bool visit[MAX][MAX];

pair<pair<int, int>, int> init;

struct Robot
{
	int x;
	int y;
	int d;
	int cnt;
};

void input()
{
	cin >> N >> M;
	cin >> init.first.first >> init.first.second >> init.second;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cin >> board[i][j];
		}
	}
}

int nextDirection(int d)
{
	if (d == 0) return 3;
	else return d - 1;
}

void bfs()
{
	queue<Robot> q;
	int x = init.first.first;
	int y = init.first.second;
	int d = init.second;
	int cnt = 1;
	q.push({ x,y,d,cnt });
	visit[x][y] = true;
	while (!q.empty())
	{
		// 현재 로봇 청소기 상태
		x = q.front().x;
		y = q.front().y;
		d = q.front().d;
		cnt = q.front().cnt;
		q.pop();

		// 4방향을 탐색해본다.
		int notFound = 0;
		for (int i = 0; i < 4; i++)
		{
			int nd = nextDirection(d);
			int nx = x + dx[nd];
			int ny = y + dy[nd];

			if (nx < 0 || ny < 0 || nx >= N || ny >= M) continue;
			// 청소할 공간을 찾았다면 탈출
			if (board[nx][ny] == 0 && visit[nx][ny] == false)
			{
				visit[nx][ny] = true;
				q.push({ nx,ny,nd,cnt + 1 });
				break;
			}
			notFound++;
			d = nd;
		}

		// 4방향 모두 청소할 공간을 찾지 못했다면
		if (notFound == 4)
		{
			int nx = x - dx[d];
			int ny = y - dy[d];

			if (nx < 0 || ny < 0 || nx >= N || ny >= M)
			{
				cout << cnt << endl;
				return;
			}

			if (board[nx][ny] == 1)
			{
				cout << cnt << endl;
				return;
			}

			q.push({ nx, ny, d, cnt });
		}
	}
}

void solve()
{
	input();
	bfs();
}

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

 

'Algorithm' 카테고리의 다른 글

[백준 11779] 최소비용 구하기2  (0) 2021.11.15
[백준 13549] 숨바꼭질3  (0) 2021.11.14
[백준 9205] 맥주 마시면서 걸어가기  (0) 2021.11.13
[백준 6593] 상범 빌딩  (0) 2021.11.11
[백준 2573] 빙산  (0) 2021.11.10
문제 풀이

 

생각보다 어려웠고, 알고 나니까 정말 괜찮은 문제라고 생각된다. 나중에 다시 풀어봐야겠다.

 

문제를 요약하면,

자신의 집에서 출발하는데 50미터씩 걸어갈 때마다 맥주를 마셔야 한다.

최대 가지고 다닐 수 있는 맥주의 개수는 20개라 총 1000미터를 다닐 수 있다.

중간에 편의점에 도착하면 맥주의 개수를 다시 20개로 충전할 수 있으므로 중간에 편의점을 만나면 그때부터 다시 1000미터 반경으로 갈 수 있다. 페스티벌에 도착할 수 있다면 해피를 아니면 세드를 표시한다.

 

문제에서는 (x, y) 좌표로 주어진다.

보통 좌표나 연결된 노드가 주어져서 그래프 자료구조를 구현해서 문제를 풀었지만

좌표 형태로 푸는 것은 생소한 경험이라 어려웠다.

 

간단히 생각해보면 집에서 출발했을 때 최대로 갈 수 있는 지점들은 정해져 있다.

맥주의 개수가 20개이므로 최대 1000미터를 갈 수 있다는 말이기 때문에 반경 1000미터의 좌표들을 전부 구해서 해당 지점과 그 지점을 그래프로 표현하면 된다.

 

중요한 점은 어떠한 지점을 도달하게 되면 다시 그 지점에서 다시 반경 1000미터를 옮길 수 있다.

편의점이라면 충전이 되는 것이고, 만약에 그곳이 페스티벌이라면 우리는 답을 찾은 것이기 때문이다.

 

따라서 전체 지점을 확인하면서 반경 1000미터 이내에 있는 지점들끼리 그래프로 연결하고,

그래프 탐색 알고리즘을 돌려서 페스티벌에 도착했는지만 체크하면 해결할 수 있다.

 

소스 코드
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

const int MAX = 105;

int n;
vector<int> adj[MAX];
bool visit[MAX];

void dfs(int node)
{
	visit[node] = true;
	for (int i = 0; i < adj[node].size(); i++)
	{
		int next = adj[node][i];
		if (!visit[next])
		{
			dfs(next);
		}
	}
}

// 맨해튼 거리 계산
int getDistance(pair<int, int> a, pair<int, int> b)
{
	return abs(a.first - b.first) + abs(a.second - b.second);
}

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

	int tc;
	cin >> tc;
	while (tc--)
	{
		for (int i = 0; i < MAX; i++)
		{
			vector<int>().swap(adj[i]);
			visit[i] = false;
		}

		cin >> n;
		vector<pair<int, int> > coord(n + 2);
		for (int i = 0; i < n + 2; i++)
		{
			cin >> coord[i].first >> coord[i].second;
		}

		// 맥주 20개로 갈 수 있는 범위를 추린다.
		for (int i = 0; i < n + 2; i++)
		{
			for (int j = i + 1; j < n + 2; j++)
			{
				if (getDistance(coord[i], coord[j]) <= 1000)
				{
					adj[i].push_back(j);
					adj[j].push_back(i);
				}
			}
		}

		dfs(0);
		// 도착할 수 있는 지점에 도착했다면 그 곳은 방문했다는 것이다.
		if (visit[n + 1])
			cout << "happy\n";
		else
			cout << "sad\n";
	}
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 13549] 숨바꼭질3  (0) 2021.11.14
[백준 14503] 로봇 청소기  (0) 2021.11.14
[백준 6593] 상범 빌딩  (0) 2021.11.11
[백준 2573] 빙산  (0) 2021.11.10
[백준 3036] 링  (0) 2021.11.09
문제 풀이

 

3차원에서 상범이가 탈출할 수 있으면 최단 경로를 리턴하고, 탈출할 수 없다면 Trapped!라고 출력하면 된다.

 

기존에는 2차원 배열에서 상,하,좌,우만 구현했다면 이번에는 위층과 아래층을 갈 수 있도록 만들어야 한다.

const int dx[] = { 0,0,-1,1,0,0 };
const int dy[] = { 1,-1,0,0,0,0 };
const int dz[] = { 0,0,0,0,-1,1 };

따라서 6방향으로 나눠서 구현해준다.

 

기존 BFS 코드를 3차원으로 구현해주기만 하면 문제를 해결할 수 있다.

주의할 점은 다른 칸으로 이동할 때 방문체크랑 '#'이 아닌 곳으로 가야 하면서 동시에 몇 번만에 그 위치에 왔는지 기록을 해야 한다.

 

아래의 구현은 i = Z, j = X(행), k = Y(열)로 구현했다.

소스 코드

 

#include <iostream>
#include <queue>
#include <algorithm>
#include <tuple>
using namespace std;

const int MAX = 31;
const int dx[] = { 0,0,-1,1,0,0 };
const int dy[] = { 1,-1,0,0,0,0 };
const int dz[] = { 0,0,0,0,-1,1 };

int L, R, C;
char board[MAX][MAX][MAX];
bool visit[MAX][MAX][MAX];

tuple<int, int, int> first, last; // (z,x,y)

struct Info
{
	int z;
	int x;
	int y;
	int cnt;
};

void bfs()
{
	fill(visit[0][0], visit[0][0] + MAX * MAX * MAX, false);
	queue<Info> q;
	
	q.push({ get<0>(first), get<1>(first), get<2>(first), 0 });
	visit[get<0>(first)][get<1>(first)][get<2>(first)] = true;
	while (!q.empty())
	{
		int z = q.front().z;
		int x = q.front().x;
		int y = q.front().y;
		int cnt = q.front().cnt;
		q.pop();

		if (x == get<1>(last) && y == get<2>(last) && z == get<0>(last))
		{
			cout << "Escaped in " << cnt << " minute(s).\n";
			return;
		}

		for (int i = 0; i < 6; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];
			int nz = z + dz[i];
			if (nx < 0 || ny < 0 || nz < 0 || nx >= R || ny >= C || nz >= L) continue;
			if (board[nz][nx][ny] != '#' && visit[nz][nx][ny] == false)
			{
				visit[nz][nx][ny] = true;
				q.push({ nz,nx,ny,cnt + 1 });
			}
		}
	}

	cout << "Trapped!\n";
	return;
}

void input()
{
	for (int i = 0; i < L; i++)
	{
		for (int j = 0; j < R; j++)
		{
			for (int k = 0; k < C; k++)
			{
				cin >> board[i][j][k];
				if (board[i][j][k] == 'S')
					first = { i,j,k };
				else if (board[i][j][k] == 'E')
					last = { i,j,k };
			}
		}
	}
}

void solve()
{
	while (true)
	{
		cin >> L >> R >> C;
		if (L == 0 && R == 0 && C == 0) break;
		input();
		bfs();
	}

}

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

	solve();
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 14503] 로봇 청소기  (0) 2021.11.14
[백준 9205] 맥주 마시면서 걸어가기  (0) 2021.11.13
[백준 2573] 빙산  (0) 2021.11.10
[백준 3036] 링  (0) 2021.11.09
[백준 1037] 약수  (0) 2021.11.09
문제 풀이

 

문제를 읽고 아래와 같이 요약을 했다.

1. 빙산을 언제까지 녹일 수 있을까?

2. 빙산을 어떻게 녹일까?

3. 언제 빙산이 2덩어리 이상으로 나누어질까?

 

1번부터 보자.

빙산을 언제까지 녹일 수 있을까?

빙산이 2차원 배열에서의 높이가 모두 0이 되어버리면 빙산은 다 녹았다고 판단할 수 있다.

간단하게 말해서 하나라도 0보다 큰 숫자(높이)가 있다면 아직 빙산이 다 녹지 않은 것이다.

 

2번을 보자.

빙산을 어떻게 녹일까?

빙산은 녹을 때 자신의 위치에서 상, 하, 좌, 우로 바닷물이 인접해있으면 그 수만큼 빙산의 높이를 줄이게 된다.

그런데 한 가지 주의할 점이 칸 마다 4방향을 확인하면서 녹이게 되면 안 된다.

A라는 칸에서 4방향 해서 녹인 빙산으로 갱신하면 그 다음 B라는 칸에서 A 라는 칸과 인접해있을 경우,

B 라는 칸도 영향을 받을 수 있기 때문이다.

 

빙산이라는 건, 한 번에 빙산 덩어리가 녹는 거지 개별적으로 녹는 것이 아니기 때문에,

녹이기 전의 빙산 구조와 녹인 후의 빙산 구조 2개를 저장할 수 있는 자료구조가 필요하다.

2개의 2차원 배열을 선언하면 해결할 수 있다.

 

3번을 보자.

언제 빙산이 2덩어리가 될까?

간단히 생각해서 아직 녹지 않은 빙산을 찾아서 그 빙산으로부터 4방향을 탐색하면서 방문을 한다.

즉, DFS를 돌린 수만큼이 결국 빙산의 덩어리 수다.

 

소스 코드

 

#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX = 301;
const int dx[] = { 0,0,-1,1 };
const int dy[] = { 1,-1,0,0 };

int N, M;
int board[MAX][MAX];     // 현재 빙산의 상태
int prevBoard[MAX][MAX]; // 기존의 빙산 기억
bool visit[MAX][MAX];

void input()
{
	cin >> N >> M;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cin >> board[i][j];
		}
	}
}

void dfs(int x, int y)
{
	visit[x][y] = true;
	for (int i = 0; i < 4; i++)
	{
		int nx = x + dx[i];
		int ny = y + dy[i];
		if (nx < 0 || ny < 0 || nx >= N || ny >= M) continue;
		if (visit[nx][ny] == false && board[nx][ny] > 0)
		{
			dfs(nx, ny);
		}
	}
}

bool checkIce()
{
	//빙산이 2덩이이상으로 나눠지는지
	fill(visit[0], visit[0] + MAX * MAX, false);

	int cnt = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (visit[i][j] == false && board[i][j] > 0)
			{
				dfs(i, j);
				cnt++;
			}
		}
	}

	return cnt >= 2;
}

bool isMelt()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (board[i][j] > 0)
				return false;
		}
	}
	return true;
}

void iceMelt()
{
	for (int i = 0; i < N; i++)
		for (int j = 0; j < M; j++)
			prevBoard[i][j] = board[i][j];

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (prevBoard[i][j] > 0)
			{
				// 자신을 기점으로 4방향의 바닷물을 체크한다.
				int cnt = 0;
				for (int d = 0; d < 4; d++)
				{
					int nx = i + dx[d];
					int ny = j + dy[d];
					if (nx < 0 || ny < 0 || nx >= N || ny >= M) continue;
					if (prevBoard[nx][ny] == 0) cnt++;
				}

				// 인접한 바닷물 개수만큼 녹인다.
				board[i][j] -= cnt;
				if (board[i][j] < 0) board[i][j] = 0;
			}
		}
	}
}

void solve()
{
	input();

	int year = 0;
	while (isMelt() == false)
	{
		year++;

		iceMelt();

		if (checkIce() == true)
		{
			cout << year << endl;
			return;
		}
	}

	// 빙산이 녹을 때까지 나눠지지 않은 경우
	cout << "0\n";
}

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

	solve();
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 9205] 맥주 마시면서 걸어가기  (0) 2021.11.13
[백준 6593] 상범 빌딩  (0) 2021.11.11
[백준 3036] 링  (0) 2021.11.09
[백준 1037] 약수  (0) 2021.11.09
[백준 2665] 미로만들기  (0) 2021.11.08
문제 풀이

 

간단한 수학 문제다.

첫 번째 링을 한 바퀴 돌리면 그 링은 몇 바퀴 도는지 기약 분수 형태로 출력한다.

기약 분수 형태로 출력하고자 한다면 유클리드 호제법을 떠올리면 된다.

어떤 분수를 기약 분수 형태로 만들고 싶을 때 최대 공약수로 나누면 기약 분수 형태를 만들 수 있기 때문이다.

 

소스 코드
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int gcd(int a, int b)
{
	if (b == 0) return a;
	else return gcd(b, a % b);
}

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

	int n;
	cin >> n;
	vector<int> v(n);
	for (int i = 0; i < n; i++) cin >> v[i];
	for (int i = 1; i < n; i++)
	{
		int g = gcd(v[0], v[i]);
		int A = v[0] / g;
		int B = v[i] / g;
		cout << A << '/' << B << '\n';
	}
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 6593] 상범 빌딩  (0) 2021.11.11
[백준 2573] 빙산  (0) 2021.11.10
[백준 1037] 약수  (0) 2021.11.09
[백준 2665] 미로만들기  (0) 2021.11.08
[백준 14221] 편의점  (0) 2021.11.04
문제 풀이

 

재밌는 문제다.

1과 N을 제외한 진짜 약수가 모두 주어졌을 때, N을 구하는 문제.

 

12의 약수를 생각해보자.

{1, 2, 3, 4, 6, 12}

문제에서 1과 N을 제외한 진짜 약수가 주어진다고 하면 아마 다음과 같을 것이다.

{2, 3, 4, 6}

위의 수열은 섞여있을 수도 있다. 그렇지만 진짜 약수들만 주어지므로 요소는 똑같다.

어떻게 N을 찾을 수 있을까?

 

간단하다.

가장 작은 수랑 가장 큰 수를 곱하면 그것이 즉 N이다.

2 x 6 = 12 (우리가 구하려고 하는 N이다)

 

따라서 수열이 주어지면 가장 작은 수랑 큰 수를 곱하면 답이다.

 

소스 코드

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

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

	int n;
	cin >> n;
	vector<int> v(n);
	for (int i = 0; i < n; i++) cin >> v[i];
	int a = *min_element(v.begin(), v.end());
	int b = *max_element(v.begin(), v.end());
	cout << a * b << endl;
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 2573] 빙산  (0) 2021.11.10
[백준 3036] 링  (0) 2021.11.09
[백준 2665] 미로만들기  (0) 2021.11.08
[백준 14221] 편의점  (0) 2021.11.04
[백준 18352] 특정 거리의 도시 찾기  (0) 2021.11.04
문제 풀이

 

(1, 1) 시작점에서 (n, n) 도착점에 도착하기 위해서 몇 번의 검은 방을 부셔야 하는지 최소의 횟수를 구하는 문제다.

 

단순히 BFS 알고리즘을 돌리기에는 에러가 있다.

(3, 3) 의 좌표를 보자.

단순히 BFS를 돌리게 되면 (3,3)은 이전의 (2,3)이나 (3,2)로부터 부신 횟수에 + 1을 하여서 2가 될 수 있지만,

그림을 보면 (3,3)도 1번만 부셔서 갈 수 있는 곳이 된다.

 

기존에 방문을 했더라도 더 빠른 경로가 있다면 그 경로로 값을 바꿔주거나 갱신을 시켜줘야 한다는 것이다.

만약 내 앞의 방이 흰방이면, 방문을 했더라도 더 빠른 경로라면 더 빠른 경로로 갱신하고 다시 살펴본다.

만약 내 앞의 방이 검은방이면, 방문을 했더라도 더 빠른 경로라면 더 빠른 경로로 갱신하고 다시 살펴본다.

이미 방문을 했더라도 더 빠른 경로로 계속 갱신하면서 출구를 찾아나간다.

 

단순한 BFS의 프레임에서 벗어난 새로운 방법의 문제였다.

소스 코드

 

#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;

const int MAX = 51;
const int INF = 987654321;
const int dx[] = { -1,1,0,0 };
const int dy[] = { 0,0,-1,1 };

int n;
int board[MAX][MAX];
int visit[MAX][MAX]; // visit[a][b] = c, (a,b) 까지 오는데 검은방을 흰방으로 바꾼 갯수가 c개

// 핵심은 이미 방문한 좌표라도 더 적은 경로가 있다면 재방문하는 것
void bfs()
{
	fill(visit[0], visit[0] + MAX * MAX, INF);
	queue<pair<int, int> > q;
	q.push({ 0,0 });
	visit[0][0] = 0;
	while (!q.empty())
	{
		int x = q.front().first;
		int y = q.front().second;
		q.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];
			if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
			// 흰방
			if (board[nx][ny] == 1)
			{
				if (visit[nx][ny] > visit[x][y])
				{
					visit[nx][ny] = visit[x][y];
					q.push({ nx,ny });
				}
			}
			// 검은방이면 부수고 지나갈때
			else
			{
				if (visit[nx][ny] > visit[x][y] + 1)
				{
					visit[nx][ny] = visit[x][y] + 1;
					q.push({ nx,ny });
				}
			}
		}
	}
}

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

	cin >> n;
	for (int i = 0; i < n; i++)
	{
		string str;
		cin >> str;
		for (int j = 0; j < str.length(); j++)
		{
			board[i][j] = str[j] - '0';
		}
	}

	bfs();
	cout << visit[n - 1][n - 1] << endl;
	return 0;
}

'Algorithm' 카테고리의 다른 글

[백준 3036] 링  (0) 2021.11.09
[백준 1037] 약수  (0) 2021.11.09
[백준 14221] 편의점  (0) 2021.11.04
[백준 18352] 특정 거리의 도시 찾기  (0) 2021.11.04
[백준 10282] 해킹  (0) 2021.10.31

+ Recent posts