본문 바로가기

삼성전자 알고리즘/정올

10. 2788 : 도약

원본 출처 : http://jungol.co.kr/bbs/board.php?bo_table=pbank&wr_id=2048&sca=50&sfl=wr_subject&stx=%EB%8F%84%EC%95%BD&sop=and

 

JUNGOL | 도약 > 문제은행

제한시간: 1000 ms    메모리제한: 64 MB 해결횟수: 258 회    시도횟수: 421 회    개구리가 연못 위에서 놀고 있다. 개구리는 N개의 연잎 들을 이용해서 이리저리 뛰어놀고 있다. 개구리가 뛰는 장면을 보던 강빈이는 개구리가 도약을 하는 경우가 얼마나 있는지 궁금해졌다. 여기서 도약은 아래 조건을 만족하는 경우를 말한다. 1. 개구리가 뛴 거리가 이전에 뛴 거리 이상 뛰지만 그 2배보다 멀리 뛰지는 않는다. 2. 개구리는 오른쪽으로만

jungol.co.kr

문제 풀이 : 

3중 포문 풀이 : 

//도약(3중루프)
#include <stdio.h>
int N;
int arr[1001];
int sorted[1002];

void merge(int list[], int left, int mid, int right) {
	int i, j, k, l;
	i = left;
	j = mid + 1;
	k = left;

	/* 분할 정렬된 list의 합병 */
	while (i <= mid && j <= right) {
		if (list[i] <= list[j])
			sorted[k++] = list[i++];
		else
			sorted[k++] = list[j++];
	}

	// 남아 있는 값들을 일괄 복사
	if (i > mid) {
		for (l = j; l <= right; l++)
			sorted[k++] = list[l];
	}
	// 남아 있는 값들을 일괄 복사
	else {
		for (l = i; l <= mid; l++)
			sorted[k++] = list[l];
	}

	// 배열 sorted[](임시 배열)의 리스트를 배열 list[]로 재복사
	for (l = left; l <= right; l++) {
		list[l] = sorted[l];
	}
}
// 합병 정렬
void merge_sort(int list[], int left, int right) {
	int mid;

	if (left < right) {
		mid = (left + right) / 2; // 중간 위치를 계산하여 리스트를 균등 분할 -분할(Divide)
		merge_sort(list, left, mid); // 앞쪽 부분 리스트 정렬 -정복(Conquer)
		merge_sort(list, mid + 1, right); // 뒤쪽 부분 리스트 정렬 -정복(Conquer)
		merge(list, left, mid, right); // 정렬된 2개의 부분 배열을 합병하는 과정 -결합(Combine)
	}
}

int main(void) {
	int cnt = 0;
	int jump1, jump2;
	scanf("%d", &N);
	for (int i = 0; i < N; i++) {
		scanf("%d", &arr[i]);
	}
	merge_sort(arr, 0, N - 1);                                                //오름차순으로 정렬 https://gmlwjd9405.github.io/2018/05/08/algorithm-merge-sort.html
	for (int start = 0; start < N - 2; start++) {                            //첫번째 점프 시작 위치
		for (int first = start + 1; first < N - 1; first++) {                  //첫번째 점프 도착 위치
			jump1 = arr[first] - arr[start];                                //첫번째 점프 거리
			for (int second = first + 1; second < N; second++) {               //두번째 점프 도착 위치
				//개구리는 이전 보다 많이 뛰지만 2배 이상을 뛰진 않는다.
				jump2 = arr[second] - arr[first];                           //두번째 점프 거리
				//첫번째 점프 거리 이상에서 첫번째 점프 거리 2배 이내인 경우를 카운트
				if (jump2 >= jump1 && jump2 <= jump1 * 2) cnt++;
				//2배를 초과 한 경우를 제외 시키기
				if (jump2 > jump1 * 2) break;
			}
		}
	}
	printf("%d", cnt);
	return 0;
}


2중 포문 + 이진탐색 풀이 :

//도약(3중루프)
#include <stdio.h>
int N;
int arr[1001];
int sorted[1002];

void merge(int list[], int left, int mid, int right) {
	int i, j, k, l;
	i = left;
	j = mid + 1;
	k = left;

	/* 분할 정렬된 list의 합병 */
	while (i <= mid && j <= right) {
		if (list[i] <= list[j])
			sorted[k++] = list[i++];
		else
			sorted[k++] = list[j++];
	}

	// 남아 있는 값들을 일괄 복사
	if (i > mid) {
		for (l = j; l <= right; l++)
			sorted[k++] = list[l];
	}
	// 남아 있는 값들을 일괄 복사
	else {
		for (l = i; l <= mid; l++)
			sorted[k++] = list[l];
	}

	// 배열 sorted[](임시 배열)의 리스트를 배열 list[]로 재복사
	for (l = left; l <= right; l++) {
		list[l] = sorted[l];
	}
}
// 합병 정렬
void merge_sort(int list[], int left, int right) {
	int mid;

	if (left < right) {
		mid = (left + right) / 2; // 중간 위치를 계산하여 리스트를 균등 분할 -분할(Divide)
		merge_sort(list, left, mid); // 앞쪽 부분 리스트 정렬 -정복(Conquer)
		merge_sort(list, mid + 1, right); // 뒤쪽 부분 리스트 정렬 -정복(Conquer)
		merge(list, left, mid, right); // 정렬된 2개의 부분 배열을 합병하는 과정 -결합(Combine)
	}
}

int lower(int s, int e, int d)
{
	int m;
	int back_up = -1;
	while (s <= e)
	{
		m = (s + e) / 2;
		if (arr[m] >= d)
		{
			back_up = m;
			e = m - 1;    //있어도 끝까지 왼쪽에서 찾아봐야해 
		}
		else {
			s = m + 1;
		}
	}
	return back_up;
}
int upper(int s, int e, int d)
{
	int m;
	int back_up = -1;
	while (s <= e)
	{
		m = (s + e) / 2;

		if (arr[m] <= d)
		{
			back_up = m;
			s = m + 1;    //있어도 끝까지 오른쪽에서 찾아봐야해
		}
		else
		{
			e = m - 1;
		}
	}
	return back_up;
}

int main(void) {
	int cnt = 0;
	int jump1, jump2;
	scanf("%d", &N);
	for (int i = 0; i < N; i++) {
		scanf("%d", &arr[i]);
	}
	merge_sort(arr, 0, N - 1);                                              //오름차순으로 정렬 https://gmlwjd9405.github.io/2018/05/08/algorithm-merge-sort.html
	//BS로 풀어보기
	int lo, up, s, e;
	for (int start = 0; start < N - 2; start++) {                            //첫번째 점프 시작 위치
		for (int first = start + 1; first < N - 1; first++) {                    //첫번째 점프 도착 위치
			jump1 = arr[first] - arr[start];
			s = jump1 + arr[first];
			e = jump1 * 2 + arr[first];
			lo = lower(first + 1, N - 1, s);
			if (lo != -1 && arr[lo] <= e) {
				up = upper(first + 1, N - 1, e);
				cnt += up - lo + 1;
			}
			//printf("start =%d , jump1 =%d, s=%d, e=%d, lo=%d, up=%d\n", arr[start], arr[first], s, e, lo, up);
		}
	}
	printf("%d\n", cnt);
	return 0;
}


: 3중 포문 풀이와 이진 탐색 풀이가 있는데 이진 탐색이 압도적으로 성능이 좋다.

이진탐색의 경우 2중 포문 안에 이진 탐색이 있는데 1중 포문에서 첫번째 점프 시작 위치를 돌아가며 시도하고 2중 포문에서 첫번째 점프가 도착하는 지점( = 두번째 점프의 시작 지점)을 정한 뒤 이진탐색으로 두번째 점프가 도착하는 지점을 탐색 하면 된다.

'삼성전자 알고리즘 > 정올' 카테고리의 다른 글

12. 1141 : 불쾌한 날  (0) 2019.06.28
11. 1318 : 못생긴 수  (0) 2019.06.16
9. 2581 : 예산  (0) 2019.06.16
8. 1889 : N Queen [DFS]  (0) 2019.06.16
7. 1169 : 주사위 던지기1[DFS]  (0) 2019.06.16