모든 입력은 base condition으로 수렴해야 한다.

 

인자로 어떤 것을 받고 어디까지 계산한 후 자기 자신에게 넘겨줄지 명확하게 정해야 한다.

 

모든 재귀 함수는 반복문만으로 동일한 동작을 할 수 있다.

반복문으로 구현했을때에 비해 코드가 간결해지지만 메모리와 시간에서 손해를 본다.

 

재귀함수가 자기 자신을 부를 때 스택 영역에 계속 누적이 된다.

 

재귀함수를 절차적으로 계속 따라들어가며 생각하면 답이없다. 절차지향적인 사고를 탈피해야 한다.

 

재귀함수 예제문제들을 풀어보니 문제에 귀납적인 내용이 적혀있다. 이걸 토대로 구현하면 될 것 같다.

작거나 큰 문제를 확장시키거나 쪼개간다는 점에서 DP와 개념은 비슷한데 memoization이 사용되지 않는다는 점이 차이인 것 같다.

 

연습 문제

 

1629: 곱셈

더보기
#include <iostream>

using namespace std;
using ll = long long;

ll powmod(ll a, ll b, ll c)
{
	if (b == 1) return a % c;

	ll val = powmod(a, b / 2, c);
	val = val * val % c;

	if (b % 2 == 0) return val;
	else return val * a % c;
}

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

	ll a, b, c;

	cin >> a >> b >> c;

	cout << powmod(a, b, c);
	
	return 0;
}

 

a^b = a^b/2 * a^b/2

(a*b)%c = (a%c)*(b%c)%c

위 두개를 알아야 한다. 그리고 b가 홀수인 경우 a를 한번 더 곱해줘야 한다는 걸 주의해야 한다.

큰 문제를 작은 문제로 쪼개서 해결한다는 점에서 뭔가 DP랑 유사한 것 같다.

 

11729: 하노이 탑 이동 순서

더보기
#include <iostream>

using namespace std;

void tower_of_hanoi(int n, int start, int dest)
{
	if (n == 1)
	{
		cout << start << " " << dest << '\n';
		return;
	}

	tower_of_hanoi(n - 1, start, 6 - start - dest);
	cout << start << " " << dest << '\n';
	tower_of_hanoi(n - 1, 6 - start - dest, dest);
	return;
}

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

	int n;

	cin >> n;

	cout << (1 << n) - 1 << '\n';
	tower_of_hanoi(n, 1, 3);
	
	return 0;
}

 

n이 성립되면 n-1도 성립되는것을 이해해야 한다.

n-1을 2번, n을 3번, n-1을 3번으로 옮긴다는 것을 알면 금방 풀 수 있는 문제이다.

즉, 귀납적 사고법이 반드시 필요한 문제.

 

1074: Z

더보기
#include <iostream>

using namespace std;

int z_search(int n, int r, int c)
{
	if (n == 0)	return 0;

	int half = 1 << (n - 1);

	if (r < half && c < half) return z_search(n - 1, r, c);
	else if (r < half && c >= half) return half * half + z_search(n - 1, r, c - half);
	else if (r >= half && c < half) return 2 * half * half + z_search(n - 1, r - half, c);
	else return 3 * half * half + z_search(n - 1, r - half, c - half);
}

int main()
{
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	// r행 c열
	int n, r, c;

	cin >> n >> r >> c;

	cout << z_search(n, r, c);
	
	return 0;
}

 

재귀의 n값마다 행과 열의 사분면을 파악해서 재귀 호출이 달라져야한다.

예를들어 3사분면에 있다면 1-2분면의 값을 모두 더하고 나머지값은 더 작은 사분면으로 판단시키기 위해 값을 조정하여 재귀를 호출한다.

n=1일때의 사분면 판정을 생각하면 확장이 쉽다.

 

기본 문제

 

1780: 종이의 개수

더보기
#include <iostream>

using namespace std;

int paper[2200][2200];
int cnt[3];

bool same_paper_judge(int x, int y, int n)
{
	int t = paper[x][y];

	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			if (t != paper[x + i][y + j])
			{
				return false;
			}
		}
	}

	return true;
}

void recursion(int x, int y, int n)
{
	if (same_paper_judge(x, y, n))
	{
		cnt[paper[x][y] + 1]++;
		return;
	}

	int div = n / 3;

	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			recursion(x + div * i, y + div * j, div);
		}
	}
}
int main()
{
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	int n;
	
	cin >> n;

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

	recursion(0, 0, n);

	for (int i = 0; i < 3; ++i)
	{
		cout << cnt[i] << '\n';
	}
	
	return 0;
}

 

9개로 나누어 분할정복 하는것이 포인트

 

1992: 쿼드 트리

더보기
#include <iostream>
#include <string>
#include <vector>

using namespace std;

string quad[65];
vector<char> answer;

bool same_judge(int x, int y, int n)
{
	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			if (quad[x][y] != quad[x + i][y + j])
			{
				return false;
			}
		}
	}

	return true;
}

void quad_tree(int x, int y, int n)
{
	if (same_judge(x, y, n))
	{
		answer.push_back(quad[x][y]);
		return;
	}
	int div = n / 2;

	answer.push_back('(');

	for (int i = 0; i < 2; ++i)
	{
		for (int j = 0; j < 2; ++j)
		{
			quad_tree(x + div * i, y + div * j, div);
		}
	}
	answer.push_back(')');
}
int main()
{
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	int n;
	
	cin >> n;

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

	quad_tree(0, 0, n);

	for (auto& a : answer)
	{
		cout << a;
	}
	
	return 0;
}

 

4개로 분할 정복 하면서 중간에 괄호를 끼워넣는게 포인트

 

2630: 색종이 만들기

더보기
#include <iostream>

using namespace std;

int paper[128][128];
int answer[2]; // 흰0 파1

bool same_judge(int x, int y, int n)
{
	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			if (paper[x][y] != paper[x + i][y + j])
			{
				return false;
			}
		}
	}

	return true;
}

void recursion(int x, int y, int n)
{
	if (same_judge(x, y, n))
	{
		answer[paper[x][y]]++;
		return;
	}
	int div = n / 2;

	for (int i = 0; i < 2; ++i)
	{
		for (int j = 0; j < 2; ++j)
		{
			recursion(x + div * i, y + div * j, div);
		}
	}
}
int main()
{
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	int n;
	
	cin >> n;

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

	recursion(0, 0, n);

	for (int i = 0; i < 2; ++i)
	{
		cout << answer[i] << '\n';
	}
	
	return 0;
}

 

'알고리즘 > 바킹독의 실전 알고리즘' 카테고리의 다른 글

[0x0D] 시뮬레이션  (0) 2022.08.01
[0x0C] 백트래킹  (0) 2022.07.31
[0x0A] DFS  (0) 2022.07.30
[0x09] BFS  (0) 2022.07.28
[0x08] 스택의 활용  (0) 2022.07.25

BFS의 큐가 스택으로 바뀌었다는 점 말고는 구현에서 차이가 없다.

다만 최단 거리를 계산할 땐 DFS를 사용할 수 없다.

그렇기에 다차원 배열에서 BFS 대신 DFS를 굳이 써야할 일은 없다.

그래프와 트리에서 사용하는 개념이다.

'알고리즘 > 바킹독의 실전 알고리즘' 카테고리의 다른 글

[0x0C] 백트래킹  (0) 2022.07.31
[0x0B] 재귀  (0) 2022.07.30
[0x09] BFS  (0) 2022.07.28
[0x08] 스택의 활용  (0) 2022.07.25
[0x07] 덱(Deque)  (0) 2022.07.25

기본적인 BFS의 구현은 노드와 방문 리스트를 이용한다.

BFS의 특성상 큐에 쌓이는 순서는 반드시 거리순이다.

 

연습 문제

 

1926: 그림

더보기
#include <iostream>
#include <queue>

using namespace std;

int canvas[501][501];
int visited[501][501];
int width, height;

pair<bool, int> bfs(int& _x, int& _y)
{
	if (visited[_y][_x] || !canvas[_y][_x])
	{
		return make_pair(false, 0);
	}

	// 좌 상 우 하
	int dx[4] = { -1, 0, 1, 0 };
	int dy[4] = { 0, -1, 0, 1 };
	queue<pair<int, int>> q;
	int pic_size = 0;

	visited[_y][_x] = 1;
	q.push({ _x, _y });

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

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

			if (nx < 0 || nx >= width || ny < 0 || ny >= height || !canvas[ny][nx])
			{
				continue;
			}
			if (!visited[ny][nx])
			{
				visited[ny][nx] = 1;
				q.push({ nx, ny });
			}
		}
	}
	return make_pair(true, pic_size);
}

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int pic = 0, pic_size = 0;

	cin >> height >> width;

	for (int y = 0; y < height; ++y)
	{
		for (int x = 0; x < width; ++x)
		{
			int p;
			cin >> p;
			canvas[y][x] = p;
		}
	}

	for (int y = 0; y < height; ++y)
	{
		for (int x = 0; x < width; ++x)
		{
			pair<int, int> t = bfs(x, y);
			if (t.first)
			{
				pic++;
				pic_size = pic_size > t.second ? pic_size : t.second;
			}
		}
	}

	cout << pic << '\n' << pic_size;

	return 0;
}

 

가장 기초적인 BFS

 

2178: 미로 탐색

더보기
#include <iostream>
#include <string>
#include <queue>

using namespace std;

int maze[101][101];
int dist[101][101];
int width, height;

int bfs(int _x, int _y)
{
	// 좌 상 우 하
	int dx[4] = { -1, 0, 1, 0 };
	int dy[4] = { 0, -1, 0, 1 };
	queue<pair<int, int>> q;

	dist[_y][_x] = 1;
	q.push({ _x, _y });

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

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

			if (nx < 0 || nx >= width || ny < 0 || ny >= height || !maze[ny][nx])
			{
				continue;
			}
			if (dist[ny][nx] == -1)
			{
				dist[ny][nx] = dist[cur.second][cur.first] + 1;
				q.push({ nx, ny });
			}
		}
	}
	return dist[height-1][width-1];
}

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int answer;

	cin >> height >> width;


	for (int y = 0; y < height; ++y)
	{
		string s;
		cin >> s;
		for (int x = 0; x < width; ++x)
		{
			maze[y][x] = s[x] - '0';
			dist[y][x] = -1;
		}
	}

	answer = bfs(0, 0);

	cout << answer;

	return 0;
}

 

간선의 가중치가 모두 1인 그래프의 최단거리는 BFS로 간단히 구현할 수 있다.

 

7576: 토마토 

더보기
#include <iostream>
#include <queue>

using namespace std;

int field[1001][1001];
int dist[1001][1001];

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int width, height, answer = 0;

	// 좌 상 우 하
	int dx[4] = { -1, 0, 1, 0 };
	int dy[4] = { 0, -1, 0, 1 };
	queue<pair<int, int>> q;

	cin >> width >> height;


	for (int y = 0; y < height; ++y)
	{
		for (int x = 0; x < width; ++x)
		{
			cin >> field[y][x];
			dist[y][x] = 0;

			if (field[y][x] == 1)
			{
				q.push({ x, y });
			}
			else if (field[y][x] == 0)
			{
				dist[y][x] = -1;
			}
		}
	}

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

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

			if (nx < 0 || nx >= width || ny < 0 || ny >= height || dist[ny][nx] >= 0 || field[ny][nx])
			{
				continue;
			}
			dist[ny][nx] = dist[cur.second][cur.first] + 1;
			q.push({ nx, ny });
		}
	}

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			if (dist[y][x] == -1)
			{
				cout << "-1";
				return 0;
			}
			answer = answer > dist[y][x] ? answer : dist[y][x];
		}
	}

	cout << answer;

	return 0;
}

 

시작점이 여러개인 BFS. 모든 시작점을 큐에 넣고 똑같이 BFS를 돌면 된다.

bfs가 끝난 후 최장거리 및 토마토의 고립여부 확인을 위해 완전 탐색을 실시한다.

 

7569: 토마토

더보기
#include <iostream>
#include <queue>
#include <tuple>

using namespace std;

int field[101][101][101];
int dist[101][101][101];

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int width, height, depth, answer = 0;

	// 좌 상 우 하 앞 뒤
	int dx[6] = { -1, 0, 1, 0, 0, 0 };
	int dy[6] = { 0, -1, 0, 1, 0, 0 };
	int dz[6] = { 0, 0, 0, 0, 1, -1 };
	queue<tuple<int, int, int>> q;

	cin >> width >> height >> depth;


	for (int z = 0; z < depth; ++z)
	{
		for (int y = 0; y < height; ++y)
		{
			for (int x = 0; x < width; ++x)
			{
				cin >> field[x][y][z];
				dist[x][y][z] = 0;

				if (field[x][y][z] == 1)
				{
					q.push({x, y, z});
				}
				else if (field[x][y][z] == 0)
				{
					dist[x][y][z] = -1;
				}
			}
		}
	}

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

		for (int i = 0; i < 6; ++i)
		{
			int nx = get<0>(cur) + dx[i];
			int ny = get<1>(cur) + dy[i];
			int nz = get<2>(cur) + dz[i];

			if (nx < 0 || nx >= width || ny < 0 || ny >= height || nz < 0 || nz >= depth || dist[nx][ny][nz] >= 0 || field[nx][ny][nz])
			{
				continue;
			}
			dist[nx][ny][nz] = dist[get<0>(cur)][get<1>(cur)][get<2>(cur)] + 1;
			q.push({ nx, ny, nz });
		}
	}

	for (int z = 0; z < depth; z++)
	{
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; ++x)
			{
				if (dist[x][y][z] == -1)
				{
					cout << "-1";
					return 0;
				}
				answer = answer > dist[x][y][z] ? answer : dist[x][y][z];
			}
		}
	}

	cout << answer;

	return 0;
}

 

3차원 확장판. x, y, z를 좀더 직관적으로 사용 및 볼수있게 수정함

 

4179: 불!

더보기
#include <iostream>
#include <string>
#include <queue>

using namespace std;

char field[1001][1001];
int dist_fire[1001][1001];
int dist_jh[1001][1001];

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int width, height;

	// 좌 상 우 하
	int dx[4] = { -1, 0, 1, 0 };
	int dy[4] = { 0, -1, 0, 1 };
	queue<pair<int, int>> q_fire;
	queue<pair<int, int>> q_jh;

	cin >> height >> width;

	for (int y = 0; y < height; ++y)
	{
		string s;
		cin >> s;

		for (int x = 0; x < width; ++x)
		{
			field[y][x] = s[x];
			dist_fire[y][x] = -1;
			dist_jh[y][x] = -1;

			if (field[y][x] == 'F')
			{
				q_fire.push({ x,y });
				dist_fire[y][x] = 0;
			}
			else if (field[y][x] == 'J')
			{
				q_jh.push({ x,y });
				dist_jh[y][x] = 0;
			}
		}
	}

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

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

			if (nx < 0 || nx >= width || ny < 0 || ny >= height)
			{
				continue;
			}
			if (field[ny][nx] == '#' || dist_fire[ny][nx] >= 0)
			{
				continue;
			}

			dist_fire[ny][nx] = dist_fire[cur.second][cur.first] + 1;
			q_fire.push({ nx, ny });
		}
	}

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

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

			// 이번엔 범위를 벗어나면 탈출 성공.
			if (nx < 0 || nx >= width || ny < 0 || ny >= height)
			{
				cout << dist_jh[cur.second][cur.first] + 1;
				return 0;
			}
			if (field[ny][nx] == '#' || dist_jh[ny][nx] >= 0)
			{
				continue;
			}
			if (dist_fire[ny][nx] != -1 && dist_fire[ny][nx] <= dist_jh[cur.second][cur.first] + 1)
			{
				continue;
			}

			dist_jh[ny][nx] = dist_jh[cur.second][cur.first] + 1;
			q_jh.push({ nx, ny });
		}
	}

	cout << "IMPOSSIBLE";

	return 0;
}

 

시작점이 두종류인 BFS. 지훈이와 불에 대한 BFS를 각각 수행함으로써 해결이 가능하다.

우선 불에대한 BFS를 수행해서 각 칸에 불이 전파되는 시간을 다 구해둔다.

이후 지훈이에 대한 BFS 수행시 각 칸의 값보다 작은 경우만 이동할 수 있다.

보통 이미 방문을 했거나 특정 값인 경우 continue로 건너뛰었는데 이 문제는 불이 붙은 시간을 확인해서 필요에 따라 continue를 하면 된다.

방문 테이블을 따로 쓴다.

 

두 종류의 BFS에서 BFS를 수행할 때 어느 한쪽이 독립적이지 않고 서로에게 영향을 준다면 위와 같은 방법으로는 풀 수 없고 백트래킹으로 해결해야한다.

 

1697: 숨바꼭질

더보기
#include <iostream>
#include <queue>

using namespace std;

int dist[100001];

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	queue<int> q;
	int n, k;

	cin >> n >> k;

	dist[n] = 0;
	q.push(n);

	while (!q.empty())
	{
		int pos = q.front();
		q.pop();

		if (pos == k)
		{
			cout << dist[pos];
			return 0;
		}

		for (auto nxt : { pos - 1, pos + 1, pos * 2 })
		{
			if (nxt < 0 || nxt > 100000)
			{
				continue;
			}
			if (dist[nxt])
			{
				continue;
			}

			dist[nxt] = dist[pos] + 1;
			q.push(nxt);
		}
	}

	return 0;
}

 

수빈이가 갈 수있는 좌표인 x-1, x+1, x*2를 큐에 넣고 돌리면 된다.

범위가 넘는건 당연히 continue 처리를 하면 되고 방문여부도 확인해주면 된다.

 

기본 문제

 

1012: 유기농 배추

더보기
#include <iostream>
#include <queue>

using namespace std;

int visited[51][51];
int cabbage_field[51][51];
int width, height;

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

int bfs(int& x, int& y)
{
	if (!cabbage_field[x][y] || visited[x][y])
	{
		return 0;
	}

	queue<pair<int, int>> q;

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

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

		for (int i = 0; i < 4; ++i)
		{
			int nx = current_pos.first + dx[i];
			int ny = current_pos.second + dy[i];

			if (nx < 0 || nx >= width || ny < 0 || ny >= height)
			{
				continue;
			}
			if (!visited[nx][ny] && cabbage_field[nx][ny] == 1)
			{
				visited[nx][ny] = 1;
				q.push({ nx, ny });
			}
		}
	}

	return 1;
}

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int t;

	cin >> t;

	while (t--)
	{
		int k, answer = 0;

		cin >> width >> height >> k;

		for (int x = 0; x < width; ++x)
		{
			fill(cabbage_field[x], cabbage_field[x] + height, 0);
			fill(visited[x], visited[x] + height, 0);
		}

		while (k--)
		{
			int x, y;

			cin >> x >> y;

			cabbage_field[x][y] = 1;
		}

		for (int x = 0; x < width; x++)
		{
			for (int y = 0; y < height; ++y)
			{
				answer += bfs(x, y);
			}
		}
		cout << answer << '\n';
	}

	return 0;
}

 

기본적인 BFS

 

10026: 적록색약

더보기
#include <iostream>
#include <string>
#include <queue>

using namespace std;

string picture[101];
int ordinary_visited[101][101];
int special_visited[101][101];

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

int bfs(int (*visited)[101], int len, bool special = false)
{
	int answer = 0;

	for (int x = 0; x < len; ++x)
	{
		for (int y = 0; y < len; ++y)
		{
			if (visited[x][y]) {
				continue;
			}

			queue<pair<int, int>> q;

			visited[x][y] = 1;
			q.push({ x, y });
			answer++;

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

				for (int i = 0; i < 4; ++i)
				{
					int nx = current_pos.first + dx[i];
					int ny = current_pos.second + dy[i];

					if (nx < 0 || nx >= len || ny < 0 || ny >= len)
					{
						continue;
					}
					// special
					if (special)
					{
                    	// 153 = G(71) + R(82)
						if (!visited[nx][ny] && ((picture[ny][nx] == picture[current_pos.second][current_pos.first])
							|| (picture[ny][nx] + picture[current_pos.second][current_pos.first] == 153)))
						{
							visited[nx][ny] = 1;
							q.push({ nx, ny });
						}

					}
					// ordinary
					else {
						if (!visited[nx][ny] && (picture[ny][nx] == picture[current_pos.second][current_pos.first]))
						{
							visited[nx][ny] = 1;
							q.push({ nx, ny });
						}
					}
				}
			}
		}
	}

	return answer;
}

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int n;
	
	cin >> n;

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

	cout << bfs(ordinary_visited, n) << " ";
	cout << bfs(special_visited, n, true);

	return 0;
}

 

bfs 함수 재활용좀 해보려고 디폴트 매개변수를 썼더니 중간에 가독성이 확 떨어지는 한줄이 생겼다

보통의 경우는 보통의 bfs인데 적록색약인 경우 R=G로 봐야하기 때문에 둘의 아스키코드 값을 더해서 판별함

 

7562: 나이트의 이동

더보기
#include <iostream>
#include <queue>

using namespace std;

int table[301][301];

int bfs(pair<int, int>& start, pair<int, int>& dest, int& len)
{
	// 좌2 상2 우2 하2 x, y
	pair<int, int> move[8] = { {-2, 1}, {-2, -1}, {-1, -2}, {1, -2}, {2, -1}, {2, 1}, {1, 2}, {-1, 2} };
	queue<pair<int, int>> q;

	q.push(start);
	table[start.first][start.second] = 0;

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

		if (cur == dest)
		{
			return table[cur.first][cur.second];
		}

		for (int i = 0; i < 8; ++i)
		{
			int dx = cur.first + move[i].first;
			int dy = cur.second + move[i].second;

			if (dx < 0 || dx >= len || dy < 0 || dy >= len)
			{
				continue;
			}
			if (table[dx][dy] >= 0)
			{
				continue;
			}

			q.push({ dx, dy });
			table[dx][dy] = table[cur.first][cur.second] + 1;
		}
	}

	return 0;
}

void init_table(int len)
{
	for (int i = 0; i < len; ++i)
	{
		fill(table[i], table[i] + len, -1);
	}
}

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int tc;

	cin >> tc;

	while (tc--)
	{
		int l;
		pair<int, int> current, target;

		cin >> l;
		cin >> current.first >> current.second;
		cin >> target.first >> target.second;

		init_table(l);

		cout << bfs(current, target, l) << '\n';
	}

	return 0;
}

 

기본적인 BFS 문제

 

5427: 불

더보기
#include <iostream>
#include <string>
#include <queue>

using namespace std;

int fire_dist[1001][1001];
int sk_dist[1001][1001];
string s[1001];

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

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

	cin >> tc;

	while (tc--)
	{
		int width, height;

		queue<pair<int, int>> fire_q;
		queue<pair<int, int>> sk_q;

		bool isEscape = false;

		cin >> width >> height;

		for (int y = 0; y < height; ++y)
		{
			cin >> s[y];
			for (int x = 0; x < width; ++x)
			{
				fire_dist[x][y] = 1000001;
				sk_dist[x][y] = -1;

				if (s[y][x] == '*')
				{
					fire_q.push({ x, y });
					fire_dist[x][y] = 0;
				}
				else if (s[y][x] == '@')
				{
					sk_q.push({ x, y });
					sk_dist[x][y] = 0;
				}
			}
		}

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

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

				if (nx < 0 || nx >= width || ny < 0 || ny >= height)
				{
					continue;
				}
				if (s[ny][nx] == '#' || fire_dist[nx][ny] <= fire_dist[cur.first][cur.second] + 1)
				{
					continue;
				}
				fire_q.push({ nx, ny });
				fire_dist[nx][ny] = fire_dist[cur.first][cur.second] + 1;
			}

		}

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

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

				if (nx < 0 || nx >= width || ny < 0 || ny >= height)
				{
					cout << sk_dist[cur.first][cur.second] + 1 << '\n';
					isEscape = true;
					break;
				}
				if (s[ny][nx] == '#' || sk_dist[cur.first][cur.second] + 1 >= fire_dist[nx][ny] || sk_dist[nx][ny] >= 0)
				{
					continue;
				}
				sk_q.push({ nx, ny });
				sk_dist[nx][ny] = sk_dist[cur.first][cur.second] + 1;
			}
		}
		if (!isEscape)
		{
			cout << "IMPOSSIBLE" << '\n';
		}
	}

	return 0;
}

 

불! 이랑 거의 동일한 문제임에도 불구하고 1시간 11분이 걸렸다.

구현의 95%정도는 30분안에 해결했는데 사소한 부분을 놓치니까 40분이 넘었다.

1000*1000 사이즈라서 최대값을 100만으로 줬어야하는데 1001로 줘서 25%에서 틀렸고 string 자체를 x,y 좌표방식으로 쓰려면 y,x 순서를 바꿔서 써야함에도 자꾸 틀리게 써서 오류도 많이 났다.

 

2583: 영역 구하기

더보기
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

int paper[101][101];
int visited[101][101];

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

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int width, height, n;
	int p_num = 0;
	queue<pair<int, int>> q;
	vector<int> answer;

	cin >> height >> width >> n;

	while (n--)
	{
		int lx, ly, rx, ry;

		cin >> lx >> ly >> rx >> ry;

		for (int x = lx; x < rx; ++x)
		{
			for (int y = ly; y < ry; ++y)
			{
				paper[x][y] = 1;
			}
		}
	}

	for (int x = 0; x < width; ++x)
	{
		for (int y = 0; y < height; ++y)
		{
			if (paper[x][y] || visited[x][y])
			{
				continue;
			}
			queue<pair<int, int>> q;
			int p_size = 0;

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

			p_num++;

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

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

					if (nx < 0 || nx >= width || ny < 0 || ny >= height)
					{
						continue;
					}
					if (paper[nx][ny] || visited[nx][ny])
					{
						continue;
					}
					q.push({ nx,ny });
					visited[nx][ny] = 1;
				}
			}
			answer.push_back(p_size);
		}
	}

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

	cout << p_num << '\n';
	for (auto& ans : answer)
	{
		cout << ans << " ";
	}

	return 0;
}

 

2667: 단지번호붙이기

더보기
#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

int visited[26][26];

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

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int n, h_num = 0;
	string house[26];
	queue<pair<int, int>> q;
	vector<int> answer;

	cin >> n;

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

	for (int x = 0; x < n; ++x)
	{
		for (int y = 0; y < n; ++y)
		{
			if (!(house[x][y]-'0') || visited[x][y])
			{
				continue;
			}
			queue<pair<int, int>> q;
			int h_size = 0;

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

			h_num++;

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

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

					if (nx < 0 || nx >= n || ny < 0 || ny >= n)
					{
						continue;
					}
					if (!(house[nx][ny]-'0') || visited[nx][ny])
					{
						continue;
					}
					q.push({ nx,ny });
					visited[nx][ny] = 1;
				}
			}
			answer.push_back(h_size);
		}
	}

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

	cout << h_num << '\n';
	for (auto& ans : answer)
	{
		cout << ans << '\n';
	}

	return 0;
}

 

기본적인 BFS

 

5014: 스타트링크

더보기
#include <iostream>
#include <queue>

using namespace std;

int visited[1000001];

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	// f:층수 s:현재위치 g:목적지 u:위로 u층 d:아래로 d층

	int f, s, g, u, d;
	queue<int> q;

	cin >> f >> s >> g >> u >> d;

	fill(visited, visited + f + 1, -1);

	q.push(s);
	visited[s] = 0;

	while (!q.empty())
	{
		int cur = q.front();
		q.pop();

		if (cur == g)
		{
			cout << visited[cur];
			return 0;
		}

		if (cur - d >= 1)
		{
			if (visited[cur - d] == -1)
			{
				q.push(cur - d);
				visited[cur - d] = visited[cur] + 1;
			}
		}
		if (cur + u <= f)
		{
			if (visited[cur + u] == -1)
			{
				q.push(cur + u);
				visited[cur + u] = visited[cur] + 1;
			}
		}
	}

	cout << "use the stairs";

	return 0;
}

 

2468: 안전 영역

더보기
#include <iostream>
#include <queue>

using namespace std;

int area[101][101];
int visited[101][101];

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

void init_visited(int len)
{
	for (int i = 0; i < len; ++i)
	{
		fill(visited[i], visited[i] + len + 1, 0);
	}
}

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int n, answer = 0;
	int hst_h = 0;
	queue<pair<int, int>> q;

	cin >> n;

	for (int x = 0; x < n; ++x)
	{
		for (int y = 0; y < n; ++y)
		{
			cin >> area[x][y];
			hst_h = hst_h > area[x][y] ? hst_h : area[x][y];
		}
	}

	for (int h = 0; h < hst_h; ++h)
	{
		int cnt = 0;
		init_visited(n);

		for (int x = 0; x < n; ++x)
		{
			for (int y = 0; y < n; ++y)
			{
				if (area[x][y] <= h || visited[x][y])
				{
					continue;
				}
				q.push({ x, y });
				visited[x][y] = 1;
				cnt++;

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

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

						if (nx < 0 || nx >= n || ny < 0 || ny >= n)
						{
							continue;
						}
						if (area[nx][ny] <= h || visited[nx][ny])
						{
							continue;
						}
						q.push({ nx, ny });
						visited[nx][ny] = 1;
					}
				}

			}
		}
		answer = answer > cnt ? answer : cnt;
	}

	cout << answer;

	return 0;
}

 

높이에 따라 bfs를 수행할 때 마다 값이 달라지기때문에 각 높이별로 bfs를 모두 수행해 주어 최고값을 산출해야 한다.

처음에 예제보고 n이하는 다 패스처리하고 예제 ac길래 매우 쉽다 생각했는데 반례랑 글좀 찾아보니 예제를 일부러 혼동하기 쉽게 만들어 둔것 같음. 아니면 내가 빡대가리거나

 

6593: 상범빌딩

더보기
#include <iostream>
#include <string>
#include <queue>
#include <tuple>

using namespace std;

string area[31][31];
int dist[31][31][31];

// 좌 우 상 하 앞 뒤
const int dx[6] = { -1, 0, 1, 0, 0, 0 };
const int dy[6] = { 0, -1, 0, 1, 0, 0 };
const int dz[6] = { 0, 0, 0, 0, -1, 1 };

int main()
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	while (true)
	{
		int width, height, depth;
		int answer = 0;
		queue<tuple<int, int, int>> q;
		// l, c, r
		cin >> depth >> height >> width;

		if (depth + width + height == 0)
		{
			return 0;
		}

		// dist 초기화
		for (int x = 0; x < width; ++x)
		{
			for (int y = 0; y < height; ++y)
			{
				fill(dist[x][y], dist[x][y] + depth, -1);
			}
		}

		// area 초기화
		for (int z = 0; z < depth; ++z)
		{
			for (int y = 0; y < height; ++y)
			{
				cin >> area[z][y];
				for (int x = 0; x < width; ++x)
				{
					if (area[z][y][x] == 'S')
					{
						q.push({ x, y, z });
						dist[x][y][z] = 0;
					}
				}
			}
		}

		while (!q.empty())
		{
			int curX = get<0>(q.front());
			int curY = get<1>(q.front());
			int curZ = get<2>(q.front());
			q.pop();

			if (area[curZ][curY][curX] == 'E')
			{
				answer = dist[curX][curY][curZ];
			}

			for (int i = 0; i < 6; ++i)
			{
				int nx = curX + dx[i];
				int ny = curY + dy[i];
				int nz = curZ + dz[i];

				if (nx < 0 || nx >= width || ny < 0 || ny >= height || nz < 0 || nz >= depth)
				{
					continue;
				}
				if (area[nz][ny][nx] == '#' || dist[nx][ny][nz] >= 0)
				{
					continue;
				}
				q.push({ nx, ny, nz });
				dist[nx][ny][nz] = dist[curX][curY][curZ] + 1;
			}

		}
		if (answer)
		{
			cout << "Escaped in " << answer << " minute(s)." << '\n';
		}
		else
		{
			cout << "Trapped!" << '\n';
		}

	}

	return 0;
}

 

토마토 3차원이랑 개념은 같음. 다행히 금방 짰음

'알고리즘 > 바킹독의 실전 알고리즘' 카테고리의 다른 글

[0x0B] 재귀  (0) 2022.07.30
[0x0A] DFS  (0) 2022.07.30
[0x08] 스택의 활용  (0) 2022.07.25
[0x07] 덱(Deque)  (0) 2022.07.25
[0x06] 큐  (0) 2022.07.25

연습문제 : BOJ 10799 [쇠막대기], BOK 2504 [괄호의 값]

 

- 10799 : 쇠막대기. 45분 못풀고 해답 봤음

#include <iostream>
#include <string>
#include <stack>

using namespace std;

int main(void)
{
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	
	stack<char> pipes;

	string	str;
	size_t	str_len = 0;
	size_t	answer = 0;

	cin >> str;
	str_len = str.length();

	for (int i = 0; i < str_len; ++i)
	{
		if (str[i] == '(')
		{
			pipes.push(str[i]);
		}
		else
		{
			pipes.pop();

			if (str[i-1] == '(')
			{
				answer += pipes.size();
				// 레이저 컷팅
			}
			else
			{
				answer += 1;
				// 파이프의 끝. 한번 잘린거나 다름없어서 한개 추가해줌
			}
		}
	}

	cout << answer;

	return 0;
}

 

연습 문제

 

2493: 탑

더보기
#include <iostream>
#include <stack>

#define X first
#define Y second

using namespace std;

int N;
stack<pair<int, int>> tower;

int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	
	cin >> N;
	tower.push({ 100000001, 0 }); // 넘을 수 없는 최대치의 벽 미리 삽입
	for (int i = 1; i <= N; i++) { // 인덱스 출력을 위해 1부터 시작
		int height;
		cin >> height;
		while (tower.top().X < height) // 더 높은 이전타워 탐색
			tower.pop();
		cout << tower.top().Y << " "; // 더 높은 타워의 레이저 송수신 결과
		tower.push({ height, i }); // 현재 타워 스택에 추가
	}
}

 

바킹독님의 소스. 나중에 다시 리벤지 하는걸로

핵심은 스택에 맨 먼저 넘을수 없는 최대높이, 인덱스 0의 값을 준다

 

#include <iostream>
#include <vector>

using namespace std;

int main(void)
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int n;
	vector<int> towers;
	vector<int> temp_towers;
	vector<int> answer;

	cin >> n;

	int data;
	for (int i = 0; i < n; ++i)
	{
		cin >> data;
		towers.push_back(data);
	}

	int count = 0;

	while (true)
	{
		int tower = towers.back();
		towers.pop_back();

		temp_towers.push_back(tower);

		//count += 1;

		if (towers.empty())
		{
			break;
		}

		while (!temp_towers.empty() && temp_towers.back() < towers.back())
		{
			answer.push_back(towers.size());
			temp_towers.pop_back();
		}

		
	}

	while (!temp_towers.empty())
	{
		answer.push_back(0);
		temp_towers.pop_back();
	}

	for (int i = 0; i < n; ++i)
	{
		cout << answer.back() << " ";
		answer.pop_back();
	}

	return 0;
}

 

내가 원래 작성하던 코드

 

6198: 옥상 정원 꾸미기

더보기
#include <iostream>
#include <stack>

using namespace std;

#define MAXIMUM 1000000000

int main(void)
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	stack<pair<int, int>> building;
	int n;
	long long answer = 0;

	cin >> n;

	building.push({MAXIMUM + 1, 0});

	for (int i = 1; i <= n; ++i)
	{
		int h;
		cin >> h;

		if (h < building.top().first)
		{
			building.push({h, i});
		}
		else
		{
			while (h >= building.top().first)
			{
				answer += i - building.top().second - 1;
				building.pop();
			}
			building.push({ h, i });
		}

		// 남은 빌딩에 대한 처리
		if (i == n)
		{
			while (MAXIMUM >= building.top().first)
			{
				answer += i - building.top().second;
				building.pop();
			}
			break;
		}
	}

	cout << answer;

	return 0;
}

 

바로 위 탑의 개념을 활용함.

 

17298: 오큰수

더보기
#include <bits/stdc++.h>
using namespace std;

int a[1000000];
int ans[1000000];
int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);
  int n;
  cin >> n;
  for (int i = 0; i < n; i++) cin >> a[i];
  stack<int> S;
  for (int i = n - 1; i >= 0; i--) {
    while (!S.empty() && S.top() <= a[i]) S.pop(); // 현재수보다 스택안에 있는 큰수 싹다 정리
    if (S.empty())
      ans[i] = -1;
    else
      ans[i] = S.top();
    S.push(a[i]);
  }
  for (int i = 0; i < n; i++) cout << ans[i] << ' ';
}

아직 못풀어서 바킹독 해답코드 들고옴. 배열 역순으로 접근해서 기준 숫자보다 큰 숫자를 다 없앤다.

 

'알고리즘 > 바킹독의 실전 알고리즘' 카테고리의 다른 글

[0x0A] DFS  (0) 2022.07.30
[0x09] BFS  (0) 2022.07.28
[0x07] 덱(Deque)  (0) 2022.07.25
[0x06] 큐  (0) 2022.07.25
[0x05] 스택  (0) 2022.07.25

원소의 추가/제거 O(1)

제일 앞/뒤 원소 확인이 O(1)

나머지 원소들의 확인/변경이 원칙적으로 불가능 하지만 c++ STL deque에서는 인덱스로 원소에 접근할 수 있음

 

양쪽 끝에서 삽입과 삭제가 모두 가능하다.

Double ended Queue라는 뜻을 가지고 있음.

양방향 큐라는 느낌보다는 vector랑 비슷한데, front에도 O(1)에 추가/제거가 가능한 느낌임

deque은 모든 원소들이 메모리 상에 연속하게 배치되어 있지 않다.

 

연습 문제

 

1021: 회전하는 큐

더보기
#include <iostream>
#include <deque>
#include <algorithm>

using namespace std;

int main(void)
{
	ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

	int n, m;
	int answer = 0;

	deque<int> rq;
	deque<int> nums;

	cin >> n >> m;

	for (int i = 0; i < m; ++i)
	{
		int data;
		cin >> data;
		nums.push_back(data);
	}

	for (int i = 1; i <= n; ++i)
	{
		rq.push_back(i);
	}

	for (int num : nums)
	{
		// algorithm 헤더에 포함. 값을 찾으면 해당 위치의 iterator를 반환. 못찾으면 end.
		// 여기에 begin을 빼면 인덱스를 알 수 있음.
		int idx = find(rq.begin(), rq.end(), num) - rq.begin();
		while (num != rq.front())
		{
			// 왼쪽으로 밀기
			if (idx < (int)rq.size() - idx)
			{
				rq.push_back(rq.front());
				rq.pop_front();
			}
			else
			{
				rq.push_front(rq.back());
				rq.pop_back();
			}
			answer++;
		}
		rq.pop_front();
	}

	cout << answer;

	return 0;
}

 

접근은 괜찮게 했는데 나는 인덱스를 왼쪽으로 밀면 +1, 오른쪽으로 밀면 -1인 누적값을 하나 사용해서 덱사

이즈/2에 더해줘서 구해줬더니 정답이 아니길래 해답을 봤음.

코드 딱 두줄 추가하고 정답처리 받긴 했는데 좀 아쉽다.

 

 

'알고리즘 > 바킹독의 실전 알고리즘' 카테고리의 다른 글

[0x09] BFS  (0) 2022.07.28
[0x08] 스택의 활용  (0) 2022.07.25
[0x06] 큐  (0) 2022.07.25
[0x05] 스택  (0) 2022.07.25
[0x04] 연결 리스트  (0) 2022.07.25

+ Recent posts