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

 

5639번: 이진 검색 트리

트리를 전위 순회한 결과가 주어진다. 노드에 들어있는 키의 값은 106보다 작은 양의 정수이다. 모든 값은 한 줄에 하나씩 주어지며, 노드의 수는 10,000개 이하이다. 같은 키를 가지는 노드는 없다

www.acmicpc.net

이 문제의 경우 이진 검색 트리이긴 하지만 나는 굳이 따지자면 분할 정복이라고 봐도 될거 같다. 그 이유는 이문제의 경우

pivot을 기준으로 계속 나눠가면서 트리를 분리해서 출력을 해주어야 한다. 기존에 이진트리가 자신의 인덱스/2가 부모로 배열을 사용하는 것과 달리 특이하게 들어 왔다. 일단 이문제의 풀이는 아래 사진처럼 배열을 나누어야 했다.

#include <iostream>
using namespace std;

int arr[10005];

void post(int start, int end) {
	if (start >= end) return;
	//else if (start == end-1) {
	//	cout << arr[start] << endl;
	//	return;
	//}
	int i;
	for (i = start + 1; i < end; i++)
		if (arr[start] < arr[i]) 
			break;
	post(start + 1, i);
	post(i , end);
	cout << arr[start] << endl;
	return;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int num;
	int idx=0;
	//while (true) {
	//	cin >> num;
	//	if (num == 0) {
	//		break;
	//	}
	//	else {
	//		arr[idx] = num;
	//		idx++;
	//	}
	//}
	while (cin >> num) {
		arr[idx] = num;
		idx++;
	}
	post(0, idx);
}

일단 이 문제는 입력 방식을

	while (cin >> num) {
		arr[idx] = num;
		idx++;
	}

이렇게 작성해 주어야 했는데 그이유는 이문제는 EOF를 통해 입력의 끝을 알기 때문에 우리가 프롬프트창에서 직접 사용자 입력을 해주게 되면 cin 의 기능이 끝나지 않는다 이에 나는 테스트 할때 main함수 부분의 주석을 해제해서 사용했다.

또한 이 문제의 경우 나의 post함수에 주석 부분은 성능을 올리기 위함이지만 가독성으로 볼때는 주석처리를 하는게 맞다.

그 이유는 위에 첨부한 풀이 그림을 보았을 때 원소가 2개남았을 때 무조건 start가 leaf단계이기 때문에 더이상 분할 할 필요가 없기 때문이다 하지만 맨끝에 원소가 하나 남았을 때는 굳이 출력을 해줄 필요가 없다. 이에 나는 원소가 하나 남았다는 것

if (start >= end) return;

을 이런식으로 start  인덱스와 end 인덱스가 같을 때 함수를 return 하여 더 이상 진행되지 않도록 하였다

'백준(코테준비) > 분할정복' 카테고리의 다른 글

백준 2630  (1) 2023.05.25
백준 1764  (0) 2023.02.21
백준 1074  (0) 2023.01.09

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

 

2630번: 색종이 만들기

첫째 줄에는 전체 종이의 한 변의 길이 N이 주어져 있다. N은 2, 4, 8, 16, 32, 64, 128 중 하나이다. 색종이의 각 가로줄의 정사각형칸들의 색이 윗줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다.

www.acmicpc.net

처음에 이문제를 보았을 때 분할 정복을 사용해서 풀면은 될거같다는 느낌을 팍팍 풍기는 그림이 그려져 있었다.

이에 분할정복을 처음 사용 할때 나는 파란종이와 흰종이를 구하는 함수 2개를 만들어서 실행시켰다. 하지만 실행 시키고나니 시간초과가 발생하였다. 이에 시간 초과를 발생시키지 않기 위해서 파란색 종이를 구하면서 하얀색 종이를 구할 수 있도록 작성했다

#include <iostream>
using namespace std;
int N;
int bCnt;
int wCnt;
int arr[128][128];
int bSolution(int x, int y , int size) {
	if (size == 0) {
		return 0;
	}
	int check=0;
	for (int i = x; i < x+size; i++) {
		for (int j = y; j < y+size; j++) {
			if (arr[i][j] != 1) {
				check = 1;
				break;
			}
		}
		if (check == 1)
			break;
	}
	if (check == 1) {
		for (int i = x; i < x + size; i++) {
			for (int j = y; j < y + size; j++) {
				if (arr[i][j] != 0) {
					check = 2;
					break;
				}
			}
			if (check == 2)
				break;
		}

	}
	if (check == 0) {
		bCnt++;
	}
	else if (check == 1) {
		wCnt++;
	}
	else {
		bSolution(x, y, size / 2);
		bSolution(x, y + size / 2, size / 2);
		bSolution(x + size / 2, y, size / 2);
		bSolution(x + size / 2, y + size / 2, size / 2);
	}
	return 0;
}

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

	bSolution(0, 0, N);
	cout << wCnt<<endl;
	cout << bCnt << endl;
}

위에 코드를 보면 check라는 변수가 존재 한다 나는 check가 0일 때는 파란색종이의 갯수를 구하다 check가 1로바뀌면 흰종이를 check가 2로 해당영역이 파란종이도 흰종이도 아닌것이라고 파악했다 이경우 분할을 통해 다시 색종이의 갯수를 구한다.

	for (int i = x; i < x+size; i++) {
		for (int j = y; j < y+size; j++) {
			if (arr[i][j] != 1) {
				check = 1;
				break;
			}
		}
		if (check == 1)
			break;
	}

이부분은 해당영역에 0이 하나라도 있으면 break 한다 그럼 일단 파란종이는 아니다

	if (check == 1) {
		for (int i = x; i < x + size; i++) {
			for (int j = y; j < y + size; j++) {
				if (arr[i][j] != 0) {
					check = 2;
					break;
				}
			}
			if (check == 2)
				break;
		}

	}

이부분은 일단 파란종이가 아니니 흰종이인지 검사한다 이부분에서 0이 하나라도 발견되면 흰종이가 아니니 이제 모드를 check2로 바꾸고 분할정복을 시행한다. 확실히 회사일을 하다보니 알고리즘 실력은 줄었으나 뭔가를 생각하고 구현을 하는 능력은 올라간거 같다

'백준(코테준비) > 분할정복' 카테고리의 다른 글

백준 5639  (1) 2023.06.22
백준 1764  (0) 2023.02.21
백준 1074  (0) 2023.01.09

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

 

1764번: 듣보잡

첫째 줄에 듣도 못한 사람의 수 N, 보도 못한 사람의 수 M이 주어진다. 이어서 둘째 줄부터 N개의 줄에 걸쳐 듣도 못한 사람의 이름과, N+2째 줄부터 보도 못한 사람의 이름이 순서대로 주어진다.

www.acmicpc.net

이 문제의 경우 생각 보다 쉽게 풀리는 문제일거 같았다 일단은 어차피 탐색할거라면 사전에서 이분탐색이 효율적이라 판단 되어 이분탐색으로 각각의 리스트에 중복되는게 있는 지 없는지를 판단 하려고 하였다

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string DeutBo[500000];
string Bodo[500000];
string Answer[500000];
int binarysearch(string* a, string answer, int low, int high) {
	int mid;
	if (low > high)
		return 0;
	else {
		mid = (low + high) / 2;
		if (answer == a[mid]) {
			return 1;
		}
		else if (answer < a[mid]) 
			return binarysearch(a, answer, low, mid - 1);
		else
			return binarysearch(a, answer, mid + 1, high);
		
	}
}
int main() {
	int N, M;
	int cnt=0;
	cin >> N >> M;
	for (int i = 0; i < N; i++) {
		cin >> DeutBo[i];
	}
	sort(DeutBo, DeutBo+N);
	for (int i = 0; i < M; i++) {
		cin >> Bodo[i];
	}
	sort(Bodo, Bodo + M);
	for (int i = 0; i < M; i++) {
		if (binarysearch(DeutBo, Bodo[i], 0, M)) {
			Answer[cnt] = Bodo[i];
			cnt++;
		}
	}
	cout << cnt<<'\n';
	for (int i = 0; i < cnt; i++) {
		cout << Answer[i] << '\n';
	}
}

이문제의 경우 이분탐색의 개념만 알면 풀기 쉬운 문제였다 일다 string객체는 사전적 대소관계를 비교할 수 있으므로

이분 탐색을 통해 지속적으로 비교해주면 되는 문제였다

'백준(코테준비) > 분할정복' 카테고리의 다른 글

백준 5639  (1) 2023.06.22
백준 2630  (1) 2023.05.25
백준 1074  (0) 2023.01.09

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

 

1074번: Z

한수는 크기가 2N × 2N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다. N > 1인 경우, 배열을

www.acmicpc.net

이 문제는 앞으로 보나 뒤로 보나 분할 정복을 쓸거 같은 문제였다 처음에는 배열의 값을 분할정복을 이용해서 넣어주려했으나 이문제의 배열만큼의 크기를 선언하려면은 이문제의 메모리 제한을 넘는 배열이 생성 된다 이에 이문제는 소속 해 있는 행과 렬을 계산 해주면서 푸는 문제가 되겠다.

#include <iostream>
#include <cmath>
using namespace std;
int n, r, c;
int ans;
void solution(int row , int col , int size) {
	if (r == row && c == col) {
		cout << ans;
		return;
	}
	if (r >= row && r < row + size && c >= col && c < col +size)
	{
		solution(row, col, size / 2);//1사분면
		solution(row, col + size/2, size / 2); //2사분면
		solution(row + size/2, col, size / 2); // 3사분면
		solution(row + size / 2, col + size / 2, size / 2); //4사분면
	}
	else {
		ans += size * size;
	}
}
int main() {
	cin >> n >> r >> c;
	solution(0, 0, pow(2,n));
}

이 문제의 설명을 하자면

일단 초기값을 입력 받았을때 size의 크기의 col이 0~7 row가 0~7에 있는지 확인한다 그후 n과 r이 있는 사분면을 만났을 때 그 4분면을 4개로 쪼갠후 자신의 값이 포함되어 있는 4분면 쪽으로 들어가 답이 나올 때 까지 쪼개면서 결국에는 size가 1이 될 때까지 4분면의 4분면...을 찾아 들어간다 만약 4분면에 포함이 안되면 그 4분면의 크기만큼 ans값에 더해준다. 왜냐면 그만큼의 크기를 뛰어넘은것으로 생각되기 때문이다 

이에 이렇게 풀면 분할정복으로 이문제를 풀 수 있다

'백준(코테준비) > 분할정복' 카테고리의 다른 글

백준 5639  (1) 2023.06.22
백준 2630  (1) 2023.05.25
백준 1764  (0) 2023.02.21

+ Recent posts