'백준 알고리즘'에 해당되는 글 14건

  1. 2020.04.06 백준 2573(빙산)
  2. 2020.03.29 백준 2146 (다리 만들기)
  3. 2020.03.29 백준 15650 (N과 M(2))
  4. 2020.03.26 백준 1022(소용돌이 예쁘게 출력하기)
  5. 2020.03.26 백준 10026(적록색약)
  6. 2020.03.25 백준 1038(감소하는 수)
백준 알고리즘2020. 4. 6. 02:31

이번 문제는 빙산이라는 문제다.

하나의 덩어리였던 빙산이 매년 녹으면서 두 개의 덩어리가 될때까지 몇년이 걸리는지 구하는 문제였다.

 

이 문제에서 가장 중요한 부분은 매년 빙하를 녹이는 부분, 두 개의 덩어리인지 확인하는 부분 그리고 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않는지 확인하는 부분인것 같다.

 

1. 

매년 빙하를 녹이는 부분

우선 매년 빙하를 녹이는 부분은 year_later()라는 함수를 이용해서 구현을 했다.

저 함수에서 가장 중요한 부분은 빙하를 녹일 때 전역배열인 vec을 이용하지 않고 임시 벡터인 temp를 이용해서 계산을 하는 것이다.

만약 이렇게 구현하지 않고 전역벡터인 vec을 이용해서 계산을 하게 되면 어떤 경우가 일어나는지 확인해보자.

위 사진에서 다른 부분은 접어 두고 (3, 2)에 있는 1과 (4, 2)에 있는 5만 보도록 하자.

이때 빙하를 녹이는 작업은 (1, 1)부터 (N, M)방향으로 진행 하므로 위에 있는 1을 녹인 상태에서 5가 3면이 바다로 이루어진 상태로 계산을 하게 된다.

그렇지만 사실 (4, 2)에 있는 5는 두 면이 바다인 상태에서 녹아야 하므로 temp라는 임시 배열을 만들고 이를 계산한 후 전역 배열인 vec에 이를 복사해 주도록 구현하였다.

 

2.

두 개의 덩어리인지 확인하는 부분

이 기능은 is_divide()라는 함수를 이용해서 구현을 했다.

빙하가 두 부분으로 이루어졌다는 것은 bfs를 이용해서 인접해 있는 값을 찾을 때 적어도 2번의 bfs함수를 진행해야 한다는 뜻이므로 cnt값을 이용해서 bfs함수가 두번 진행될것 같으면(덩어리가 두개 이상이라면) true를 반환해주고 한 번만 진행한다면(한개의 덩어리로 이루어져 있다면) false를 반환해주었다.

 

3. 

전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않는지 확인하기
이 기능은 final_check()함수를 이용해서 구현하였다.

사실 이 기능은 바로 위에서 is_divide()를 이용해서 두 덩어리로 이루어져 있지 않다는 것을 확인했기 때문에 vec배열 안에 0이 아닌 다른 값이 들어 있으면 다시 빙하를 녹이도록 구현을 하였다.

 

그리고 끝으로 이 문제를 풀면서 3번의 런타임 에러가 발생했었는데 이는 final_check()함수에서 for문 안에서만 return을 해주고 함수 전체에 대해서는 return을 해주지 않아서 발생한 것이였다.

이는 visual studio같은 msvc환경에서는 문제가 되지 않지만 visual studio code같은 환경에서는 런타임 에러를 발생시킨다는 알고리즘 고수분의 말씀을 듣고 알게 되었다.

 

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int N, M;
vector<vector<int>> vec(302);
struct Dir {int y, x;};
Dir dir[4] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
bool check[302][302] = { false, };
void input() {
	int n;
	for (int i = 1; i <= N; i++) {
		vec[i].push_back(0);
		for (int j = 1; j <= M; j++) {
			cin >> n;
			vec[i].push_back(n);
		}
	}
}
void copy_vector(vector<vector<int>> temp) {
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++)
			vec[i][j] = temp[i][j];
	}
}
void year_later() {			//일년이 지나는 함수
	vector<vector<int>> temp(302);	//얼음이 녹을 때 전역벡터인 vec을 가지고 계산을 하게 되면 안된다.
	//모든 얼음이 한꺼번에 녹기 때문에 임시 벡터인 temp를 사용해야 한다.
	for (int i = 1; i <= N; i++) {
		temp[i].push_back(0);
		for (int j = 1; j <= M; j++)
			temp[i].push_back(vec[i][j]);
	}
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			int cnt = 0;
			if (vec[i][j]) {
				for (int k = 0; k < 4; k++) {
					int new_y = i + dir[k].y;
					int new_x = j + dir[k].x;
					if (new_y > 0 && new_y <= N && new_x > 0 && new_x <= M && !vec[new_y][new_x])	//몇개의 바다로 둘러 쌓여 있는지 확인한다.
						cnt++;
				}
				temp[i][j] - cnt < 0 ? temp[i][j] = 0 : temp[i][j] -= cnt;	//바다의 수만큼 빙하의 높이가 감소한다.
			}
		}
	}
	copy_vector(temp);	//계산이 끝난 temp배열을 전역 변수인 vec에 대입해준다.
}
void bfs(int y, int x) {	//빙하가 몇 개의 덩어리로 이루어져 있는지 확인하는 함수
	check[y][x] = true;
	queue<pair<int, int>> q;
	q.push({ y, x });
	while (!q.empty()) {
		int new_y = q.front().first;
		int new_x = q.front().second;
		q.pop();
		for (int i = 0; i < 4; i++) {
			int next_y = new_y + dir[i].y;
			int next_x = new_x + dir[i].x;
			if (next_y > 0 && next_y <= N && next_x > 0 && next_x <= M && !check[next_y][next_x] && vec[next_y][next_x]) {
				check[next_y][next_x] = true;
				q.push({ next_y, next_x });
			}
		}
	}
}
void reset() {
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++)
			check[i][j] = false;
	}
}
bool is_divide() {		//빙하의 덩어리가 2개 이상이라면 true를 반환하고 아니라면 false를 반환한다. 
	int cnt = 0;
	reset();
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			if (vec[i][j] && !check[i][j]) {
				if (cnt == 0) {
					cnt++;
					bfs(i, j);
				}
				else 		//끝났다는 것
					return true;
			}
		}
	}
	return false;
}
bool final_check() {
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			if (vec[i][j])		//녹을 때까지 두 덩이로 분류가 된게 아니라면
				return false;
		}
	}
    return true;        //요기서 return true를 안해줘서 계속 틀렸다.
}
int main(void) {
	cin.tie(NULL);
	ios::sync_with_stdio(false);
	cin >> N >> M;
	input();	//입력값 받는 함수
	int result = 0;		//결과값
	while (1) {
		result++;
		year_later();		//빙하가 녹는 기능을 지닌 함수
		if (is_divide())	//두 덩이로 분류가 됐다면
			break;
		if (final_check()) {	//빙하가 다 녹았는지 확인
			result = 0;
			break;
		}
	}
	cout << result << "\n";
	return 0;
}

 

'백준 알고리즘' 카테고리의 다른 글

백준 2075(N번째 큰 수)  (0) 2020.05.28
백준 2174(로봇 시뮬레이션)  (0) 2020.04.06
백준 2146 (다리 만들기)  (0) 2020.03.29
백준 15650 (N과 M(2))  (0) 2020.03.29
백준 1022(소용돌이 예쁘게 출력하기)  (0) 2020.03.26
Posted by rycbar2592
백준 알고리즘2020. 3. 29. 23:28

하나의 섬에서 다른 섬으로 갈때 최소한의 다리를 구하는 문제이다.

 

문제의 접근 방법

1. 각각의 섬을 구분 시켜준다.

2. 다리를 놓을 수 있는 섬의 테두리를 찾는다.

3. 2번에서 찾은 테두리에 다리를 놓으며 가장 적은 사다리 개수를 찾는다.

 

섬을 구분하기 위해 (1, 1)에서 (N, N)까지 bfs방법을 이용해서 섬마다 번호를 붙여주었다.

입력값이 0과 1로 들어오기 때문에 섬의 번호는 2부터 지어줬다.

divide함수를 이용해서 아래 사진과 같이 섬에 번호를 붙여주었다.

(1, 1)부터 (N, N)까지 돌면서 다리를 놓을 수 있는 위치를 찾아준다.(surround함수)

그리고 bfs2함수를 이용해서 최소의 다리를 이용해 건널 수 있는 위치를 찾아준다.

 

 

#include <iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<string.h>
using namespace std;
int N;
int map[101][101] = { 0, };
bool check[101][101] = { false, };
struct Dir {
	int y, x;
};
Dir dir[4] = { {1, 0}, {-1, 0},  {0, 1}, {0, -1} };
int result = 123456789;
void divide(int y, int x, int cnt) {		//섬마다 번호를 붙여준다.
	queue<pair<int, int>> q;
	q.push({ y, x });
	map[y][x] = cnt;
	check[y][x] = true;
	while (!q.empty()) {
		int new_y = q.front().first;
		int new_x = q.front().second;
		q.pop();
		for (int i = 0; i < 4; i++) {
			int next_y = new_y + dir[i].y;
			int next_x = new_x + dir[i].x;
			if (next_y > 0 && next_y <= N && next_x > 0 && next_x <= N && map[next_y][next_x] == 1) {
				check[next_y][next_x] = true;
				map[next_y][next_x] = cnt;
				q.push({ next_y, next_x });
			}
		}
	}
}

bool surround(int y, int x) {		//다리가 놓을 수 있는 위치라면(섬의 테두리 부분이라면)
	for (int i = 0; i < 4; i++) {
		if (!map[y + dir[i].y][x + dir[i].x])
			return true;
	}
	return false;
}

void bfs2(int y, int x) {		//bfs방식을 이용해서 다른 섬으로 가는 다리의 수를 찾아준다.
	queue<pair<pair<int, int>, int>> q;		//현재 좌표의 y, x, 몇번째 다리인지
	q.push({ {y, x}, 0 });
	int num = map[y][x];
	while (!q.empty()) {
		int new_y = q.front().first.first;
		int new_x = q.front().first.second;
		int cnt = q.front().second;
		q.pop();
		if (map[new_y][new_x] != num && map[new_y][new_x] != 0) {
			result > cnt ? result = cnt : NULL;
			return;
		}
		for (int i = 0; i < 4; i++) {
			int next_y = new_y + dir[i].y;
			int next_x = new_x + dir[i].x;
			if (next_y > 0 && next_y <= N && next_x > 0 && next_x <= N && map[next_y][next_x] != num && !check[next_y][next_x]) {
				check[next_y][next_x] = true;
				q.push({ { next_y, next_x }, cnt + 1 });
			}
		}
	}
}

void reset() {			//check배열의 초기화를 진행한다.
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++)
			check[i][j] = false;
	}
}

int main(void)
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);
	cin >> N;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			cin >> map[i][j];
		}
	}

	int cnt = 2;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			if (map[i][j] == 1)
				divide(i, j, cnt++);
		}
	}

	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			if (map[i][j] && surround(i, j)) {		//다리를 놓을 수 있는 위치라면
				bfs2(i, j);
				reset();
			}
		}
	}

	cout << result - 1 << endl;
	system("pause");
	return 0;
}

'백준 알고리즘' 카테고리의 다른 글

백준 2174(로봇 시뮬레이션)  (0) 2020.04.06
백준 2573(빙산)  (0) 2020.04.06
백준 15650 (N과 M(2))  (0) 2020.03.29
백준 1022(소용돌이 예쁘게 출력하기)  (0) 2020.03.26
백준 10026(적록색약)  (0) 2020.03.26
Posted by rycbar2592
백준 알고리즘2020. 3. 29. 22:56

재귀를 이용해서 벡터에 오름차순으로 정렬을 해준다.

 

recursive함수 탈출 조건은 재귀의 깊이가 M과 같고 출력할 배열의 크기가 M과 같다면 배열 값을 출력해주고 탈출하도록 하였다.

 

#include <iostream>
#include<algorithm>
#include<vector>
using namespace std;
int N, M;

void recursive(int depth, int num, vector<int> vec) {
	if (depth == M && vec.size() == M) {
		for (int i = 0; i < vec.size(); i++)
			cout << vec[i] << " ";
		cout << endl;
		return;
	}

	for (int i = num + 1; i <= N; i++) {
		vec.push_back(i);
		recursive(depth + 1, i, vec);
		vec.pop_back();
	}
}

int main(void)
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);
	cin >> N >> M;
	for (int i = 1; i <= N; i++) {
		vector<int> vec;
		vec.push_back(i);
		recursive(1, i, vec);
		vec.clear();
	}

	system("pause");
	return 0;
}

'백준 알고리즘' 카테고리의 다른 글

백준 2573(빙산)  (0) 2020.04.06
백준 2146 (다리 만들기)  (0) 2020.03.29
백준 1022(소용돌이 예쁘게 출력하기)  (0) 2020.03.26
백준 10026(적록색약)  (0) 2020.03.26
백준 1038(감소하는 수)  (0) 2020.03.25
Posted by rycbar2592
백준 알고리즘2020. 3. 26. 20:37

이 문제를 처음 풀때는 10000 * 10000크기인 배열 arr을 선언한 후 arr[5000][5000]을 (0, 0)으로 잡고 값을 채워나가도록 구현을 하였다.

그렇지만 메모리 제한이 128MB이기에 메모리 초과라는 결과를 얻었다.

그렇다면 arr에 모든 값을 저장하지 않고 우리가 구하려는 값만 저장해서 구현을 하면 되지 않을까 하는 생각을 하게 됐고 아래 코드는 이 방법으로 구현을 한 결과이다.

소스 코드 풀이

문제의 조건에서 입력값의 최대가 5000이므로 x, y를 좌표를 (5000, 5000)으로 잡고 소용돌이를 그리기 시작하였다.

이때 소용돌이를 그리는 방법에는 일정한 패턴이 있다.

처음 1, 2, 3, 4, 5, 6, 7을 그리기 위해서는 오 위 왼왼 아래아래의 방향으로 진행을 한다.

또한 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21을 그리기 위해서는 오오오 위위위 왼왼왼왼 아래아래아래아래의 순서로 진행이 된다.

이를 통해 다음 소용돌이를 그리기 위해서는 바로 전 단계의 소용돌이의 각자 방향 + 2번만큼 그리면 된다는 사실을 알 수 있다. (첫번째는 오가 하나였다면 다음번에는 오가 3번 그려진다.)

이러한 방법으로 모든 배열을 그리면서 우리가 원하는 범위 안에 있는 그림이 그려질 때 arr배열안에 해당 값을 저장하면 된다.

아래에 있는 start()함수가 위와 같은 기능을 하는 함수이다.

이렇게 배열에 값을 입력하고 나고 다음으로 할 일은 우리가 구한 값을 이쁘게 출력하는 것이다.

나는 이쁘게 그리는 방법으로 string을 활용했다.

배열에 값을 저장할 때 배열안에 들어가는 가장 큰 값을 leng이라는 변수에 저장을 한다.

그리고 calculate_len()이라는 함수를 이용해서 leng의 길이를 알아내고 arr안에 있는 모든 값을 leng의 길이만큼 string에 더하고 출력하는 방법으로 문제를 해결했다.

아래 사진은 arr안에 값을 대입하는 과정을 설명해놓은 사진이다.

 

 

 

#include <iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<vector>
using namespace std;
int arr[50][5] = { 0, };
struct Dir {
	int y, x, cnt;
};
Dir dir[4] = { {0, 1, 1}, {-1, 0, 1}, {0, -1, 2}, {1, 0, 2} };
int r1, r2, c1, c2, leng;
void start() {
	int num = 1;
	int y = 5000, x = 5000;
	while (1) {
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < dir[i].cnt; j++) {
				if (y - 5000 >= r1 && y - 5000 <= r2 && x - 5000 >= c1 && x - 5000 <= c2) {
					arr[y - 5000 - r1][x - 5000 - c1] = num;
					leng < num ? leng = num : NULL;
				}
				y += dir[i].y;
				x += dir[i].x;
				if (num++ == 110000000) {
					return;
				}
			}
			dir[i].cnt += 2;
		}
	}
}

int calculate_len(int num) {
	int len = 0;
	while (1) {
		if (num == 0)
			return len;
		num /= 10;
		len++;
	}
}

int main(void)
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);
	vector<string> vec(50, "");
	for (int i = 0; i < abs(r2 - r1); i++)
		vec.push_back("");
	cin >> r1 >> c1 >> r2 >> c2;
	start();

	int longest = calculate_len(leng);
	for (int i = 0; i <= abs(c2 - c1); i++) {		//가로
		for (int j = 0; j <= abs(r2 - r1); j++) {
			string tem = "";
			for (int k = 0; k <= longest - calculate_len(arr[j][i]) - 1; k++)
				tem += " ";
			tem += to_string(arr[j][i]);
			tem += " ";
			vec[j] += tem;
		}
	}

	for (int i = 0; i <= abs(r2 - r1); i++)
		cout << vec[i] << endl;
	return 0;
}

 

'백준 알고리즘' 카테고리의 다른 글

백준 2573(빙산)  (0) 2020.04.06
백준 2146 (다리 만들기)  (0) 2020.03.29
백준 15650 (N과 M(2))  (0) 2020.03.29
백준 10026(적록색약)  (0) 2020.03.26
백준 1038(감소하는 수)  (0) 2020.03.25
Posted by rycbar2592
백준 알고리즘2020. 3. 26. 20:04

이 문제는 dfs로도 해결할 수 있고 bfs로도 해결할 수 있다.

밑에 있는 코드는 그 중에서 bfs방법을 이용해서 해결한 소스 코드이다.

이 문제는 적록색약이 아닌 사람과 적록색약인 사람이 봤을 때의 구역을 둘다 구하는 것이다.

 

문제 풀이 순서

1. 적록색약이 아닌 사람이 봤을 때의 영역을 구한다.

2. 내가 사용한 전역변수의 값을 바꾸면서 적록색약의 시야로 배열 값을 변경해준다.

3. 적록색약인 사람이 봤을 때의 영역을 구한다.

 

start()함수에서는 arr배열의 전체 인덱스를 돌면서 res변수를 이용해서 영역의 수를 구해준다.

이렇게 영역의 수를 구하고 나면 reset()함수를 이용해서 전역변수 값을 초기화함과 동시에 arr배열에 저장된 R값을 G로 변경시켜준다.(적록색약의 영역을 구하기 위해)

이후에는 위 과정을 다시 반복한 후(적록색약의 영역의 수 구하기) 반환값을 출력해주면 된다.

 

#include <iostream>
#include<queue>
#include<string>
#include<string.h>
using namespace std;
int N;
char arr[101][101];
struct Dir { int y, x; };
Dir dir[4] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
bool check[101][101] = { false, };
void bfs(int y, int x) {		//bfs방법을 이용해서 하나의 영역을 구한다.
	queue<pair<int, int>> q;
	char c = arr[y][x];
	q.push({y, x} );
	check[y][x] = true;
	while (!q.empty()) {
		int new_y = q.front().first;
		int new_x = q.front().second;
		q.pop();
		for (int i = 0; i < 4; i++) {
			int next_y = new_y + dir[i].y;
			int next_x = new_x + dir[i].x;
			if (next_y > 0 && next_y <= N && next_x > 0 && next_x <= N && !check[next_y][next_x] && arr[next_y][next_x] == c) {
				check[next_y][next_x] = true;
				q.push({next_y ,next_x});
			}
		}
	}
}

int start() {		//배열의 모든부분을 돌며 영역의 수를 구한다.
	int res = 0;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			if (!check[i][j]) {
				res++;
				bfs(i, j);
			}
		}
	}
	return res;
}

void reset() {		//전역변수들을 초기화 시켜주는 함수
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			arr[i][j] == 'R' ? arr[i][j] = 'G' : NULL;
			check[i][j] = false;
		}
	}
}

int main(void)
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);
	string str;
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> str;
		for (int j = 0; j < N; j++) {
			arr[i][j + 1] = str[j];
		}
	}
	int first = start();
	reset();
	int second = start();
	cout << first << " " << second << endl;
	return 0;
}

'백준 알고리즘' 카테고리의 다른 글

백준 2573(빙산)  (0) 2020.04.06
백준 2146 (다리 만들기)  (0) 2020.03.29
백준 15650 (N과 M(2))  (0) 2020.03.29
백준 1022(소용돌이 예쁘게 출력하기)  (0) 2020.03.26
백준 1038(감소하는 수)  (0) 2020.03.25
Posted by rycbar2592
백준 알고리즘2020. 3. 25. 21:28

감소하는 수의 범위는 0 <= x <= 9876543210이다.

따라서 아무리 N이 큰 값이 들어와도 큐에 들어가 있는 수가 9876543210보다 크다면 -1값을 출력해야 한다.

이 문제를 진행하면서 가장 중요하게 봐야 하는 것은 큐에 들어가 있는 수의 마지막 자리수이다. ex) 54321의 경우 1

그렇기 때문에 가장 처음에는 한자리수인 0~9까지 큐에 집어넣은 후 풀이를 시작한다.

큐에는 0~9까지 들어가 있는 상태에서 제일 앞에 있는 0값을 num에 대입한 후 큐를 pop한다.

0의 뒤에는 어떤 수도 올 수 없으므로 0은 버린다.

다음으로는 num에 1값을 대입 하고 큐를 pop하게 되면 1뒤에 올 수 있는 수는 10이다.

그럼 이 수를 큐에 push한 후 이와 같은 방법을 계속 진행한다.

그러면 10, 20, 21, 30, 31, 32, .... 과 같은 수가 계속 큐에 들어갈 것이고 이를 계속 반복하다 보면 언젠가는 987654321까지 들어갈 것이다.

987654321이후로는 들어갈 수 있는 수가 없으므로 반복문을 탈출하게 되고 답을 출력해주면 된다.

 

'백준 알고리즘' 카테고리의 다른 글

백준 2573(빙산)  (0) 2020.04.06
백준 2146 (다리 만들기)  (0) 2020.03.29
백준 15650 (N과 M(2))  (0) 2020.03.29
백준 1022(소용돌이 예쁘게 출력하기)  (0) 2020.03.26
백준 10026(적록색약)  (0) 2020.03.26
Posted by rycbar2592