https://www.acmicpc.net/problem/9205

 

9205번: 맥주 마시면서 걸어가기

송도에 사는 상근이와 친구들은 송도에서 열리는 펜타포트 락 페스티벌에 가려고 한다. 올해는 맥주를 마시면서 걸어가기로 했다. 출발은 상근이네 집에서 하고, 맥주 한 박스를 들고 출발한다.

www.acmicpc.net

문제

송도에 사는 상근이와 친구들은 송도에서 열리는 펜타포트 락 페스티벌에 가려고 한다. 올해는 맥주를 마시면서 걸어가기로 했다. 출발은 상근이네 집에서 하고, 맥주 한 박스를 들고 출발한다. 맥주 한 박스에는 맥주가 20개 들어있다. 목이 마르면 안되기 때문에 50미터에 한 병씩 마시려고 한다. 즉, 50미터를 가려면 그 직전에 맥주 한 병을 마셔야 한다.

상근이의 집에서 페스티벌이 열리는 곳은 매우 먼 거리이다. 따라서, 맥주를 더 구매해야 할 수도 있다. 미리 인터넷으로 조사를 해보니 다행히도 맥주를 파는 편의점이 있다. 편의점에 들렸을 때, 빈 병은 버리고 새 맥주 병을 살 수 있다. 하지만, 박스에 들어있는 맥주는 20병을 넘을 수 없다. 편의점을 나선 직후에도 50미터를 가기 전에 맥주 한 병을 마셔야 한다.

편의점, 상근이네 집, 펜타포트 락 페스티벌의 좌표가 주어진다. 상근이와 친구들이 행복하게 페스티벌에 도착할 수 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 t가 주어진다. (t ≤ 50)

각 테스트 케이스의 첫째 줄에는 맥주를 파는 편의점의 개수 n이 주어진다. (0 ≤ n ≤ 100).

다음 n+2개 줄에는 상근이네 집, 편의점, 펜타포트 락 페스티벌 좌표가 주어진다. 각 좌표는 두 정수 x와 y로 이루어져 있다. (두 값 모두 미터, -32768 ≤ x, y ≤ 32767)

송도는 직사각형 모양으로 생긴 도시이다. 두 좌표 사이의 거리는 x 좌표의 차이 + y 좌표의 차이 이다. (맨해튼 거리)

출력

각 테스트 케이스에 대해서 상근이와 친구들이 행복하게 페스티벌에 갈 수 있으면 "happy", 중간에 맥주가 바닥나서 더 이동할 수 없으면 "sad"를 출력한다. 

예제 입력 1 복사

2
2
0 0
1000 0
1000 1000
2000 1000
2
0 0
1000 0
2000 1000
2000 2000

예제 출력 1 복사

happy
sad

 


풀이

  • 50m당 1병 마실 수 있으며 박스에 20개씩 들어있다. -> 최대 50 * 20 = 1000m까지 해피하게 맥주를 마시면서 락 페스티벌에 걸어갈 수 있다.
  • 처음에는 입력값이 왜 저렇게 들어가는지 이해가 안 됐었는데 문제를 꼼꼼하게 읽어보니 집, 편의점 n개, 펜타포트인 것을 확인할 수 있었다.
  • 벡터에 모든 곳의 좌표를 저장하였고, 인접리스트에 두 점의 거리가 1000 이상일 경우만 서로 간선으로 이어주었다.
  • 방문 배열의 가장 마지막이 페타포트이기 때문에 이곳의 방문 여부에 따라 출력값을 다르게 하면 된다.

 

전체 코드

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

#define MAX 102

vector<int> graph[MAX];
bool visited[MAX];

// 초기화 함수
void Init()
{
	memset(visited, false, sizeof(visited));
	for (int i = 0; i < MAX; i++)
	{
		graph[i].clear();
	}
}

// 위치간 거리 반환하는 함수
int GetDistance(pair<int, int> p1, pair<int, int> p2)
{
	return abs(p1.first - p2.first) + abs(p1.second - p2.second);
}

void Dfs(int v)
{
	visited[v] = true;
	for (int i = 0; i < graph[v].size(); i++)
	{
		int next = graph[v][i];
		if (!visited[next])
			Dfs(next);
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int T;
	cin >> T;

	while(T--)
	{
		Init();

		int n;
		vector<pair<int, int>> vec;
		cin >> n;

		for (int i = 0; i < n+2; i++)
		{
			int x, y;
			cin >> x >> y;
			vec.push_back({ x, y });
		}

		// 거리가 1000 이하일 경우 위치 연결
		for (int i = 0; i < n+2; i++)
		{
			for (int j = i+1; j < n+2; j++)
			{
				int dis = GetDistance(vec[i], vec[j]);
				if (dis <= 50 * 20)
				{
					graph[i].push_back(j);
					graph[j].push_back(i);
				}
			}
		}

		Dfs(0);	// 집부터 탐색하기

		// 도착했으면 happy, 아니면 sad
		if (visited[n+1])
			cout << "happy\n";
		else
			cout << "sad\n";

	}

	return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 2573 - 빙산  (0) 2023.01.19
[C++] 백준 2638 - 치즈  (0) 2023.01.13
[C++] 백준 2636 - 치즈  (0) 2023.01.13
[C++] 백준 2589 - 보물섬  (0) 2022.12.28
[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27

https://www.acmicpc.net/problem/2573

 

2573번: 빙산

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을

www.acmicpc.net

문제

지구 온난화로 인하여 북극의 빙산이 녹고 있다. 빙산을 그림 1과 같이 2차원 배열에 표시한다고 하자. 빙산의 각 부분별 높이 정보는 배열의 각 칸에 양의 정수로 저장된다. 빙산 이외의 바다에 해당되는 칸에는 0이 저장된다. 그림 1에서 빈칸은 모두 0으로 채워져 있다고 생각한다.

             
  2 4 5 3    
  3   2 5 2  
  7 6 2 4    
             

그림 1. 행의 개수가 5이고 열의 개수가 7인 2차원 배열에 저장된 빙산의 높이 정보

빙산의 높이는 바닷물에 많이 접해있는 부분에서 더 빨리 줄어들기 때문에, 배열에서 빙산의 각 부분에 해당되는 칸에 있는 높이는 일년마다 그 칸에 동서남북 네 방향으로 붙어있는 0이 저장된 칸의 개수만큼 줄어든다. 단, 각 칸에 저장된 높이는 0보다 더 줄어들지 않는다. 바닷물은 호수처럼 빙산에 둘러싸여 있을 수도 있다. 따라서 그림 1의 빙산은 일년후에 그림 2와 같이 변형된다.

그림 3은 그림 1의 빙산이 2년 후에 변한 모습을 보여준다. 2차원 배열에서 동서남북 방향으로 붙어있는 칸들은 서로 연결되어 있다고 말한다. 따라서 그림 2의 빙산은 한 덩어리이지만, 그림 3의 빙산은 세 덩어리로 분리되어 있다.

             
    2 4 1    
  1   1 5    
  5 4 1 2    
             

그림 2

             
      3      
        4    
  3 2        
             

그림 3

한 덩어리의 빙산이 주어질 때, 이 빙산이 두 덩어리 이상으로 분리되는 최초의 시간(년)을 구하는 프로그램을 작성하시오. 그림 1의 빙산에 대해서는 2가 답이다. 만일 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 프로그램은 0을 출력한다.

입력

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 10 이하이다. 배열에서 빙산이 차지하는 칸의 개수, 즉, 1 이상의 정수가 들어가는 칸의 개수는 10,000 개 이하이다. 배열의 첫 번째 행과 열, 마지막 행과 열에는 항상 0으로 채워진다.

출력

첫 줄에 빙산이 분리되는 최초의 시간(년)을 출력한다. 만일 빙산이 다 녹을 때까지 분리되지 않으면 0을 출력한다.

예제 입력 1 복사

5 7
0 0 0 0 0 0 0
0 2 4 5 3 0 0
0 3 0 2 5 2 0
0 7 6 2 4 0 0
0 0 0 0 0 0 0

예제 출력 1 복사

2

 


풀이

  • 2차원 배열을 탐색하기 위해 BFS를 사용하였다.
  • 빙산이 분리되었다 -> BFS가 2회 이상 실행되었을 때를 뜻한다. 이 경우 반복문을 탈출하여 분리됐을 때의 년수를 출력한다.
  • 원래는 빙산 배열을 복사하지 않고 녹여줬었는데, 빙산이 모두 녹아 바다가 되어버려 인접한 바다의 수가 증가하는 일이 발생하여 현재 상태의 빙산 배열을 따로 복사하여 바다를 탐색하였다.
  • MeltIceberg(): 만약 빙산이 분리되지 않았다면 빙산 주변에 바다가 몇 개 있는지 확인해야한다. 바다의 수만큼 빙산을 녹여주고 년수를 증가시킨다. 빙산은 음수가 될 수 없기 때문에 0 이하가 될 경우 0으로 변경해주는 조건문을 추가하였다.
void MeltIceberg()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			int cnt = 0;
			if (copyMap[i][j] == 0) continue;
			if (copyMap[i + 1][j] == 0) cnt++;
			if (copyMap[i - 1][j] == 0) cnt++;
			if (copyMap[i][j + 1] == 0) cnt++;
			if (copyMap[i][j - 1] == 0) cnt++;

			map[i][j] = map[i][j] - cnt;
			if (map[i][j] < 0) map[i][j] = 0;
		}
	}
	year++;
}
  • HasAllMelt(): 분리되지 않은 채 빙산이 모두 녹았을 경우(전부 0이 되었을 때) while문을 탈출하기 위해 사용된다.
bool HasAllMelt()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (map[i][j] != 0) return false;
		}
	}
	return true;
}

 

전체 코드

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

#define MAX 301
#define _X first
#define _Y second

int map[MAX][MAX];
int copyMap[MAX][MAX];	// 현재 배열을 저장하여 빙산을 녹일 때 사용
bool visited[MAX][MAX];
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, -1, 1 };

int N, M, year;
bool isSeparated = false;

queue<pair<int, int>> q;

void Bfs()
{
	while (!q.empty())
	{
		auto cur = q.front();
		int x = cur._X;
		int y = cur._Y;
		q.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];

			if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
			if (map[nx][ny] == 0 || visited[nx][ny]) continue;
			q.push({ nx, ny });
			visited[nx][ny] = true;
		}
	}
}

// 빙산이 다 녹았으면 참, 아니면 거짓 반환하는 함수
bool HasAllMelt()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (map[i][j] != 0) return false;
		}
	}
	return true;
}

// 빙산을 녹이는 함수
// 빙산이 존재하는 위치의 상하좌우를 탐색하여 주변 바다의 수를 저장하여 녹이고, 년수를 증가시킨다
void MeltIceberg()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			int cnt = 0;
			if (copyMap[i][j] == 0) continue;
			if (copyMap[i + 1][j] == 0) cnt++;
			if (copyMap[i - 1][j] == 0) cnt++;
			if (copyMap[i][j + 1] == 0) cnt++;
			if (copyMap[i][j - 1] == 0) cnt++;

			map[i][j] = map[i][j] - cnt;
			if (map[i][j] < 0) map[i][j] = 0;
		}
	}
	year++;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> N >> M;

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

	while (true)
	{
		if (HasAllMelt()) break;

		int cnt = 0;
		
		memset(visited, false, sizeof(visited));

		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				if (map[i][j] > 0 && !visited[i][j])
				{
					visited[i][j] = true;
					q.push({ i, j });
					Bfs();
					cnt++;
				}
				copyMap[i][j] = map[i][j];	// 현재 배열 저장
			}
		}

		if (cnt > 1)
		{
			isSeparated = true;
			break;
		}

		MeltIceberg();
	}

	if (isSeparated)
		cout << year;
	else
		cout << 0;

	return 0;
}

 

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 9205 - 맥주 마시면서 걸어가기  (0) 2023.01.19
[C++] 백준 2638 - 치즈  (0) 2023.01.13
[C++] 백준 2636 - 치즈  (0) 2023.01.13
[C++] 백준 2589 - 보물섬  (0) 2022.12.28
[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27

https://www.acmicpc.net/problem/2638

 

2638번: 치즈

첫째 줄에는 모눈종이의 크기를 나타내는 두 개의 정수 N, M (5 ≤ N, M ≤ 100)이 주어진다. 그 다음 N개의 줄에는 모눈종이 위의 격자에 치즈가 있는 부분은 1로 표시되고, 치즈가 없는 부분은 0으로

www.acmicpc.net

문제

N×M의 모눈종이 위에 아주 얇은 치즈가 <그림 1>과 같이 표시되어 있다. 단, N 은 세로 격자의 수이고, M 은 가로 격자의 수이다. 이 치즈는 냉동 보관을 해야만 하는데 실내온도에 내어놓으면 공기와 접촉하여 천천히 녹는다. 그런데 이러한 모눈종이 모양의 치즈에서 각 치즈 격자(작 은 정사각형 모양)의 4변 중에서 적어도 2변 이상이 실내온도의 공기와 접촉한 것은 정확히 한시간만에 녹아 없어져 버린다. 따라서 아래 <그림 1> 모양과 같은 치즈(회색으로 표시된 부분)라면 C로 표시된 모든 치즈 격자는 한 시간 후에 사라진다.

<그림 1>

<그림 2>와 같이 치즈 내부에 있는 공간은 치즈 외부 공기와 접촉하지 않는 것으로 가정한다. 그러므 로 이 공간에 접촉한 치즈 격자는 녹지 않고 C로 표시된 치즈 격자만 사라진다. 그러나 한 시간 후, 이 공간으로 외부공기가 유입되면 <그림 3>에서와 같이 C로 표시된 치즈 격자들이 사라지게 된다.

<그림 2>

<그림 3>

모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정한다. 입력으로 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 모눈종이의 크기를 나타내는 두 개의 정수 N, M (5 ≤ N, M ≤ 100)이 주어진다. 그 다음 N개의 줄에는 모눈종이 위의 격자에 치즈가 있는 부분은 1로 표시되고, 치즈가 없는 부분은 0으로 표시된다. 또한, 각 0과 1은 하나의 공백으로 분리되어 있다.

출력

출력으로는 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 정수로 첫 줄에 출력한다.

예제 입력 1 복사

8 9
0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0
0 0 0 1 1 0 1 1 0
0 0 1 1 1 1 1 1 0
0 0 1 1 1 1 1 0 0
0 0 1 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0

예제 출력 1 복사

4

 


풀이

  • 백준 2636 치즈(hhttps://www.acmicpc.net/problem/2636)와 유사한 문제로 치즈를 녹이기 위한 조건이 하나 추가된 문제이다.
  • 바깥쪽에 있는 치즈 중 공기와 맞닿은 곳이 두면 이상일 경우 녹여줘야하기 때문에 2차원 배열에 존재하는 모든 공기를 2로 변경하여 치즈와 맞닿은 면을 체크하고, 치즈 주변에 있는 공기면의 수를 체크하여 녹여준다.
  • 배열을 계속 탐색해야 하기 때문에 2로 변경했던 외부 공기를 다시 0으로 되돌린다.
  • 배열에 치즈 존재하지 않을 때까지 BFS를 실행하여 녹는 시간을 증가시킨다.

 

전체 코드

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

#define MAX 101
#define _X first
#define _Y second

int board[MAX][MAX];
bool visited[MAX][MAX];
int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };

int N, M, hour;

queue<pair<int, int>> q;

// 2로 변경한 외부 공기를 다시 0으로 바꿔주는 함수
void ResetAir()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (board[i][j] == 2)
				board[i][j] = 0;
		}
	}
}

// 배열에 치즈가 존재하지 않을 경우 true, 존재할 경우 false 반환
bool HasAllMelted()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			if (board[i][j] != 0)
				return false;
		}
	}
	return true;

}

// 치즈 사방에 공기가 있을 경우 카운트 증가시킨 후, 두면이 공기라면 치즈를 녹임
void MeltCheese()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			int airCount = 0;
			if (board[i][j] != 1) continue;
			if (board[i + 1][j] == 2) airCount++;
			if (board[i - 1][j] == 2) airCount++;
			if (board[i][j + 1] == 2) airCount++;
			if (board[i][j - 1] == 2) airCount++;

			if (airCount >= 2)
			{
				board[i][j] = 0;
			}
		}
	}
}

void Bfs()
{
	q.push({ 0, 0 });
	visited[0][0] = true;
	hour++;

	while (!q.empty())
	{
		pair<int, int> cur = q.front();
		int x = cur._X;
		int y = cur._Y;
		q.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];

			if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
			if (board[nx][ny] != 0 || visited[nx][ny]) continue;
			// 공기를 2로 바꿔줌
			q.push({ nx, ny });
			board[nx][ny] = 2;
			visited[nx][ny] = true;
		}
	}
}


int main()
{
	cin.tie(0); cout.tie(0);

	cin >> N >> M;

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

	while (true)
	{
		if (HasAllMelted())
		{
			cout << hour;
			break;
		}

		memset(visited, 0, sizeof(visited));
		Bfs();
		MeltCheese();
		ResetAir();
	}

	return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 9205 - 맥주 마시면서 걸어가기  (0) 2023.01.19
[C++] 백준 2573 - 빙산  (0) 2023.01.19
[C++] 백준 2636 - 치즈  (0) 2023.01.13
[C++] 백준 2589 - 보물섬  (0) 2022.12.28
[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27

https://www.acmicpc.net/problem/2636

 

2636번: 치즈

첫째 줄에는 사각형 모양 판의 세로와 가로의 길이가 양의 정수로 주어진다. 세로와 가로의 길이는 최대 100이다. 판의 각 가로줄의 모양이 윗 줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진

www.acmicpc.net

문제

아래 <그림 1>과 같이 정사각형 칸들로 이루어진 사각형 모양의 판이 있고, 그 위에 얇은 치즈(회색으로 표시된 부분)가 놓여 있다. 판의 가장자리(<그림 1>에서 네모 칸에 X친 부분)에는 치즈가 놓여 있지 않으며 치즈에는 하나 이상의 구멍이 있을 수 있다.

이 치즈를 공기 중에 놓으면 녹게 되는데 공기와 접촉된 칸은 한 시간이 지나면 녹아 없어진다. 치즈의 구멍 속에는 공기가 없지만 구멍을 둘러싼 치즈가 녹아서 구멍이 열리면 구멍 속으로 공기가 들어가게 된다. <그림 1>의 경우, 치즈의 구멍을 둘러싼 치즈는 녹지 않고 ‘c’로 표시된 부분만 한 시간 후에 녹아 없어져서 <그림 2>와 같이 된다.

<그림 1> 원래 치즈 모양

다시 한 시간 후에는 <그림 2>에서 ‘c’로 표시된 부분이 녹아 없어져서 <그림 3>과 같이 된다.

<그림 2> 한 시간 후의 치즈 모양

<그림 3> 두 시간 후의 치즈 모양

<그림 3>은 원래 치즈의 두 시간 후 모양을 나타내고 있으며, 남은 조각들은 한 시간이 더 지나면 모두 녹아 없어진다. 그러므로 처음 치즈가 모두 녹아 없어지는 데는 세 시간이 걸린다. <그림 3>과 같이 치즈가 녹는 과정에서 여러 조각으로 나누어 질 수도 있다.

입력으로 사각형 모양의 판의 크기와 한 조각의 치즈가 판 위에 주어졌을 때, 공기 중에서 치즈가 모두 녹아 없어지는 데 걸리는 시간과 모두 녹기 한 시간 전에 남아있는 치즈조각이 놓여 있는 칸의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 사각형 모양 판의 세로와 가로의 길이가 양의 정수로 주어진다. 세로와 가로의 길이는 최대 100이다. 판의 각 가로줄의 모양이 윗 줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다. 치즈가 없는 칸은 0, 치즈가 있는 칸은 1로 주어지며 각 숫자 사이에는 빈칸이 하나씩 있다.

출력

첫째 줄에는 치즈가 모두 녹아서 없어지는 데 걸리는 시간을 출력하고, 둘째 줄에는 모두 녹기 한 시간 전에 남아있는 치즈조각이 놓여 있는 칸의 개수를 출력한다.

 


풀이

  • 치즈가 다 없어질 때까지 BFS를 실행하는 문제이다.
  • 바깥쪽에 있는 치즈만 찾아내면 되기 때문에 (0, 0) 좌표부터 차례대로 탐색하고 사방에 치즈가 있을 경우 녹여준다.
  • 모두 녹기 한 시간 전에 남아있는 치즈 개수를 확인하기 위해서 BFS를 실행할 때마다 현재 남은 치즈의 개수를 temp 변수에 저장해둔다.

코드

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

#define MAX 101
#define _X first
#define _Y second

int board[MAX][MAX];
bool visited[MAX][MAX];
int n, m, hour, temp;
int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
bool check = false;

queue<pair<int, int>> q;

void Bfs()
{
	q.push({ 0, 0 });
	visited[0][0] = true;
	hour++;
	int cnt = 0;
	while (!q.empty())
	{
		pair<int, int> cur = q.front();
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int nx = cur._X + dx[i];
			int ny = cur._Y + dy[i];

			if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
			if (visited[nx][ny]) continue;
			if (board[nx][ny] == 0)
			{
				q.push({ nx, ny });
			}
			// 치즈 있으면 녹여주고 치즈 개수 증가
			else
			{
				board[nx][ny] = 0;
				cnt++;
			}
			visited[nx][ny] = true;
		}
	}

	if (cnt == 0)
		check = true;
	//  치즈가 남았다면 현재 남은 치즈 저장
	else
		temp = cnt;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> n >> m;

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> board[i][j];
		}
	}

	while (true)
	{
		if (check)
		{
			cout << hour - 1 << '\n' << temp;
			break;
		}
		memset(visited, false, sizeof(visited));
		Bfs();
	}

	return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 2573 - 빙산  (0) 2023.01.19
[C++] 백준 2638 - 치즈  (0) 2023.01.13
[C++] 백준 2589 - 보물섬  (0) 2022.12.28
[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27
[C++] 백준 7562 - 나이트의 이동  (2) 2022.12.26

https://www.acmicpc.net/problem/2589

 

2589번: 보물섬

보물섬 지도를 발견한 후크 선장은 보물을 찾아나섰다. 보물섬 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 각 칸은 육지(L)나 바다(W)로 표시되어 있다. 이 지도에서

www.acmicpc.net

문제

보물섬 지도를 발견한 후크 선장은 보물을 찾아나섰다. 보물섬 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 각 칸은 육지(L)나 바다(W)로 표시되어 있다. 이 지도에서 이동은 상하좌우로 이웃한 육지로만 가능하며, 한 칸 이동하는데 한 시간이 걸린다. 보물은 서로 간에 최단 거리로 이동하는데 있어 가장 긴 시간이 걸리는 육지 두 곳에 나뉘어 묻혀있다. 육지를 나타내는 두 곳 사이를 최단 거리로 이동하려면 같은 곳을 두 번 이상 지나가거나, 멀리 돌아가서는 안 된다.

예를 들어 위와 같이 지도가 주어졌다면 보물은 아래 표시된 두 곳에 묻혀 있게 되고, 이 둘 사이의 최단 거리로 이동하는 시간은 8시간이 된다.

보물 지도가 주어질 때, 보물이 묻혀 있는 두 곳 간의 최단 거리로 이동하는 시간을 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 보물 지도의 세로의 크기와 가로의 크기가 빈칸을 사이에 두고 주어진다. 이어 L과 W로 표시된 보물 지도가 아래의 예와 같이 주어지며, 각 문자 사이에는 빈 칸이 없다. 보물 지도의 가로, 세로의 크기는 각각 50이하이다.

출력

첫째 줄에 보물이 묻혀 있는 두 곳 사이를 최단 거리로 이동하는 시간을 출력한다.

예제 입력 1 복사

5 7
WLLWWWL
LLLWLLL
LWLWLWW
LWLWLLL
WLLWLWW

예제 출력 1 복사

8

풀이

  • BFS로 간단하게 풀리는 문제였다.
  • 보물은 최단 거리에 있어 가장 긴 시간이 걸리는 위치에 존재하기 때문에 이동할 수 있는 육지(L)를 탐색하며 상하좌우를 탐색할 수 있을 때마다 1씩 증가시킨다. 2차원 배열에서 탐색하면서 증가된 가장 큰 수를 출력하면 된다.

코드

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

#define MAX 51
#define _X first
#define _Y second

char map[MAX][MAX];
int visited[MAX][MAX] = { 0, };

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

queue<pair<int, int>> q;

int r, c;
int dis = -1;

void Bfs(int x, int y)
{
	q.push({ x, y });
	visited[x][y] = 1;

	while (!q.empty())
	{
		pair<int, int> cur = q.front();
		q.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = cur._X + dx[i];
			int ny = cur._Y + dy[i];

			if (nx < 0 || nx >= r || ny < 0 || ny >= c) continue;
			if (map[nx][ny] == 'W' || visited[nx][ny] != 0) continue;

			q.push({ nx, ny });
			visited[nx][ny] = visited[cur._X][cur._Y] + 1;
			if (dis < visited[nx][ny])
				dis = visited[nx][ny];
		}
	}


}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> r >> c;

	for (int i = 0; i < r; i++)
	{
		string str;
		cin >> str;
		for (int j = 0; j < c; j++)
		{
			map[i][j] = str[j];
		}
	}

	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			if (map[i][j] == 'L' && visited[i][j] == 0)
			{
				Bfs(i, j);
				memset(visited, 0, sizeof(visited));
			}
		}
	}

	cout << dis - 1;

	return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 2638 - 치즈  (0) 2023.01.13
[C++] 백준 2636 - 치즈  (0) 2023.01.13
[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27
[C++] 백준 7562 - 나이트의 이동  (2) 2022.12.26
[C++] 백준 11724 - 연결 요소의 개수  (0) 2022.12.26

https://www.acmicpc.net/problem/1697

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

문제

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.

수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.

출력

수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.

예제 입력 1 복사

5 17

예제 출력 1 복사

4

힌트

수빈이가 5-10-9-18-17 순으로 가면 4초만에 동생을 찾을 수 있다.


 

코드

#include <iostream>
#include <queue>

using namespace std;

#define MAX 1000001

int n, k;
int dis[MAX];
queue<int> q;

int Bfs(int start, int finish)
{
    q.push(n);
    while (!q.empty())
    {
        int cur = q.front();
        int dir[3] = { 1, -1, cur };    // 수빈이가 이동할 수 있는 방향 벡터
        q.pop();

        // 현재 위치가 동생의 위치라면 이동 횟수 반환
        if (cur == finish) return dis[cur];

        for (int i = 0; i < 3; i++)
        {
            int next = cur + dir[i];
            if (next < 0 || next >= MAX || dis[next] != 0) continue;
            q.push(next);
            dis[next] = dis[cur] + 1;
        }
    }
    return 0;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> k;

    int result = Bfs(n, k);
    cout << result;

    return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 2636 - 치즈  (0) 2023.01.13
[C++] 백준 2589 - 보물섬  (0) 2022.12.28
[C++] 백준 7562 - 나이트의 이동  (2) 2022.12.26
[C++] 백준 11724 - 연결 요소의 개수  (0) 2022.12.26
[C++] 백준 2644 - 촌수계산  (0) 2022.12.26

https://www.acmicpc.net/problem/7562

 

7562번: 나이트의 이동

체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수

www.acmicpc.net

문제

체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수 있을까?

입력

입력의 첫째 줄에는 테스트 케이스의 개수가 주어진다.

각 테스트 케이스는 세 줄로 이루어져 있다. 첫째 줄에는 체스판의 한 변의 길이 l(4 ≤ l ≤ 300)이 주어진다. 체스판의 크기는 l × l이다. 체스판의 각 칸은 두 수의 쌍 {0, ..., l-1} × {0, ..., l-1}로 나타낼 수 있다. 둘째 줄과 셋째 줄에는 나이트가 현재 있는 칸, 나이트가 이동하려고 하는 칸이 주어진다.

출력

각 테스트 케이스마다 나이트가 최소 몇 번만에 이동할 수 있는지 출력한다.

예제 입력 1 복사

3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1

예제 출력 1 복사

5
28
0

 

풀이

  • 기존 BFS/DFS에서 사용하는 방향 벡터를 나이트가 이동할 수 있는 8개의 좌표로 지정하여 BFS를 실행하였다.
  • 테스트케이스가 여러개일 수 있으므로, 케이스를 입력할 때마다 큐와 2차원 배열을 비워주고 초기화해야 한다.

코드

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

#define MAX 301
#define _X first
#define _Y second

int board[MAX][MAX];
int dx[8] = { 1, 2, 2, 1, -1, -2, -2, -1 };
int dy[8] = { 2, 1, -1, -2, -2, -1, 1, 2 };

int t, l; // 테스트 케이스, 한 변의 길이
int posX, posY, tPosX, tPosY;
queue<pair<int, int>> q;

// 배열 초기화, 큐 비워주는 함수
void Reset(int side)
{       
    for (int i = 0; i < side; i++)
    {
        for (int j = 0; j < side; j++)
        {
            board[i][j] = 0;
        }
    }
    while (!q.empty()) q.pop();
}

int Bfs()
{
    while (!q.empty())
    {
        // 이동하려는 좌표가 시작 좌표면 안 움직여도 된다.
        if (posX == tPosX && posY == tPosY) return 0;

        // 현재 위치 x, y 저장
        pair<int, int> cur = q.front();
        int x = cur._X;
        int y = cur._Y;
        q.pop();

        for (int i = 0; i < 8; i++)
        {
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 범위 벗어나거나 이미 방문했으면 무시
            if (nx < 0 || nx >= l || ny < 0 || ny >= l) continue;
            if (board[nx][ny] != 0) continue;

            // 다음 위치가 이동하려던 위치면 이동 횟수 반환
            if (nx == tPosX && ny == tPosY) return board[x][y] + 1;
            
            q.push({ nx, ny });
            board[nx][ny] = board[x][y] + 1;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> t;

    while (true)
    {
        // 테스트 케이스 횟수만큼 다 입력하면 반복문 벗어나기
        if (t == 0) break;  
        cin >> l;
        Reset(l);

        cin >> posX >> posY >> tPosX >> tPosY;
        // 현재 위치 큐에 넣고 Bfs 실행 후 이동 횟수 출력
        q.push({ posX, posY });
        int result = Bfs();
        cout << result << '\n';
        t--;
    }

    return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 2589 - 보물섬  (0) 2022.12.28
[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27
[C++] 백준 11724 - 연결 요소의 개수  (0) 2022.12.26
[C++] 백준 2644 - 촌수계산  (0) 2022.12.26
[C++] 백준 2178 - 미로 탐색  (0) 2022.12.25

https://www.acmicpc.net/problem/11724

 

11724번: 연결 요소의 개수

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주

www.acmicpc.net

 
 

문제

방향 없는 그래프가 주어졌을 때, 연결 요소 (Connected Component)의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주어진다.

출력

첫째 줄에 연결 요소의 개수를 출력한다.

예제 입력 1 복사

6 5
1 2
2 5
5 1
3 4
4 6

예제 출력 1 복사

2

예제 입력 2 복사

6 8
1 2
2 5
5 1
3 4
4 6
5 4
2 4
2 3

예제 출력 2 복사

1

풀이

  • 무방향 그래프(Undirected Graph)란 두 정점 사이에 방향이 없는 그래프를 뜻한다. 모든 정점이 연결되어 하나로 이루어질 수도 있고, 여러 개의 부분 그래프로 구성되어 있을 수도 있다.
  • 연결 요소(Connected Component)란 무방향 그래프에서 나누어진 각각의 그래프를 뜻한다. 아래 그림의 경우, 2개의 연결 요소로 이루어져 있다고 볼 수 있다.

문제 예제 1번 그래프 모습

  • 벡터를 이용한 인접리스트를 구현하여 문제를 해결하였다.
  • 방문하지 않은 정점에 대해 Dfs를 실행하였고, 해당 정점과 이어진 모든 정점을 탐색할 수 있도록 Dfs를 사용하였다.
  • 한 그래프에 대한 탐색이 종료되면 연결 요소의 개수(cnt)를 증가시킨다.

코드

#include <iostream>
#include <vector>

using namespace std;

#define MAX 1001

vector<int> vec[MAX];
bool visited[MAX];

int vertex, edge, from, to, cnt;

void Dfs(int v)
{
	visited[v] = true;

	for (int i = 0; i < vec[v].size(); i++)
	{
		int next = vec[v][i];
		if (!visited[next])
			Dfs(next);
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> vertex >> edge;

	for (int i = 1; i <= edge; i++)
	{
		cin >> from >> to;
		vec[from].push_back(to);
		vec[to].push_back(from);
	}

	for (int i = 1; i <= vertex; i++)
	{
		if (!visited[i])
		{
			Dfs(i);
			cnt++;
		}
	}

	cout << cnt;

	return 0;
}

'C++ > BFS, DFS' 카테고리의 다른 글

[C++] 백준 1697 - 숨바꼭질  (0) 2022.12.27
[C++] 백준 7562 - 나이트의 이동  (2) 2022.12.26
[C++] 백준 2644 - 촌수계산  (0) 2022.12.26
[C++] 백준 2178 - 미로 탐색  (0) 2022.12.25
[C++] 백준 2606 - 바이러스  (0) 2022.12.25

+ Recent posts