본문 바로가기
학부공부/Algorithm PS_알고리즘

07.01. 회전하는 큐 [Linked List][백준 1021]

by sonpang 2022. 2. 8.
반응형

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

 

1021번: 회전하는 큐

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가

www.acmicpc.net

 

Algorithm classification : Linked List, Data Structure, Deque

 

07.01.1. Problem

지민이는 N개의 원소를 포함하고 있는 양방향 순환 큐를 가지고 있다. 지민이는 이 큐에서 몇 개의 원소를 뽑아내려고 한다.

지민이는 이 큐에서 다음과 같은 3가지 연산을 수행할 수 있다.

  1. 첫 번째 원소를 뽑아낸다. 이 연산을 수행하면, 원래 큐의 원소가 a1, ..., ak이었던 것이 a2, ..., ak와 같이 된다.
  2. 왼쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 a2, ..., ak, a1이 된다.
  3. 오른쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 ak, a1, ..., ak-1이 된다.

큐에 처음에 포함되어 있던 수 N이 주어진다. 그리고 지민이가 뽑아내려고 하는 원소의 위치가 주어진다. (이 위치는 가장 처음 큐에서의 위치이다.) 이때, 그 원소를 주어진 순서대로 뽑아내는데 드는 2번, 3번 연산의 최솟값을 출력하는 프로그램을 작성하시오.

 

 

07.01.2. Input limit

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가 순서대로 주어진다. 위치는 1보다 크거나 같고, N보다 작거나 같은 자연수이다.

 

 

07.01.3. Output

첫째 줄에 문제의 정답을 출력한다.

 

 

07.01.4. Solution

사실 배열로 구현해도 되는 문제입니다. 그렇지만 Doubly linked list를 연습하기에 가장 쉬운 문제입니다. 배열로 구현하신다면 index 변수를 하나 두고 이용하면 됩니다. 연결 리스트도 마찬가지로 index 변수처럼 pointer를 하나 두고 node를 옮겨다니면 됩니다. 2번 연산과 3번 연산을 반복했을 때 필요한 연산횟수 중 최소값을 택하면 됩니다.

 

gcc로 돌려보신다면 valgrind memory leak도 한번 해보시면 좋을 것 같습니다.(Garbage Collector도 궁금해하실까봐 link를 달아놓겠습니다.)

2021.11.15 - [학부공부/OS_운영체제] - 13. Garbage collector [Interesting topic to study]

 

13. Garbage collector [Interesting topic to study]

안녕하세요. 오늘은 Garbage collector에 대해 알아볼까 합니다. 2021.11.12 - [학부공부/운영체제] - 10. Process(1) 10. Process(1) 안녕하세요. 오늘은 process에 대해서 알아보는 시간을 가질까 합니다. 저번..

ku320121.tistory.com

 

본격적으로 code를 보겠습니다. 사실 code는 알고리즘 카테고리에서 정리한 내용을 천천히 따라가보면 됩니다. node와 list struct를 간단하게 선언하고요. 물론 라이브러리를 가져다 써도 되지만 처음 배울때는 이렇게 만들어 쓰는게 이해도 더 잘되고 문제마다 더 적합한 자료구조를 만드는 연습도 할 수 있으니 직접 만들어 쓰는 것을 권장해드립니다.

typedef struct node{
	int data;
	struct node* next;
	struct node* prev;
} NODE;

typedef struct list{
	int size;
	NODE* head;
	NODE* rear;
} LIST;

각 함수들에 대해서도 정의를 해두고 가는게 좋겠죠.

void insert(LIST* pList, int data);
void fun1(LIST* pList);
void fun2(LIST* pList);
void fun3(LIST* pList);
int count_fun2(LIST* pList, int data);
int count_fun3(LIST* pList, int data);
void destroy(LIST* pList);

위에서 함수만 잘 정의했다면 main function에서는 잘 활용만 해주면 됩니다.

int main()
{
	int n, ans = 0, m, i, find;
	scanf("%d %d", &n, &m);
	
	LIST* l = (LIST*)malloc(sizeof(LIST));
	l->head = l->rear = NULL;
	l->size = 0;
	
	for(i = 1; i <= n; i++){
		insert(l, i);
	}
	for(i = 0; i < m; i++){
		scanf("%d", &find);
		//printf("%d %d %d", find, count_fun2(l, find), count_fun3(l, find));
		if(count_fun2(l, find) <= count_fun3(l, find)){
			while(l->head->data != find){
				fun2(l);
				ans++;
			}
			fun1(l);
		}
		else{
			while(l->head->data != find){
				fun3(l);
				ans++;
			}
			fun1(l);
		}
		//printf(" > %d\n", ans);
	}
	printf("%d", ans);
	destroy(l);
	
	return 0;
}

이제부터는 pointer에 대한 이해가 조금씩 필요합니다. list node를 추가하는 것도 작성해보고요.

void insert(LIST* pList, int data){
	NODE* newNode = (NODE*)malloc(sizeof(NODE));
	newNode->data = data;
	newNode->next = newNode->prev = NULL;
	if(!pList->head){
		pList->head = newNode;
		pList->head->next = newNode;
		pList->head->prev = newNode;
		pList->rear = pList->head; 
	}
	else{
		newNode->prev = pList->rear;
		pList->rear->next = newNode;
		pList->rear = newNode;
		pList->head->prev = pList->rear;
		pList->rear->next = pList->head;
	}
	pList->size++;
}

삭제는 모든 node를 다 지우고 list도 free시켜주면 됩니다.(해리포터의 한 대사가 떠오르네요. : dobby is free)

void destroy(LIST* pList){
	NODE* deleteNode = pList->head;
	while(!pList->head){
		pList->head = pList->head->next;
		free(deleteNode);
	}
	free(pList);
}

사실 이 정도만 작성해보면 fun1, fun2, fun3, count_fun2, count_fun3는 쉽게 작성하실 수 있습니다.

void fun1(LIST* pList){
	NODE* deleteNode = pList->head;
	pList->head = pList->head->next;
	pList->rear->next = pList->head;
	pList->head->prev = pList->rear;
	free(deleteNode);
}

void fun2(LIST* pList){
	pList->rear = pList->head;
	pList->head = pList->head->next;
}

void fun3(LIST* pList){
	pList->head = pList->rear;
	pList->rear = pList->rear->prev;
}

int count_fun2(LIST* pList, int data){
	NODE* curNode = pList->head;
	int count = 0;
	while(data != curNode->data){
		count++;
		curNode = curNode->next;
	}
	return count;	
}

int count_fun3(LIST* pList, int data){
	NODE* curNode = pList->rear;
	int count = 1;
	while(data != curNode->data){
		count++;
		curNode = curNode->prev;
	}
	return count;
}

 

 

이렇게 직접 작성해보신다면 틀릴 이유가 눈뜨고 찾아봐도 없습니다. 이제 당당하게 제출하실 일만 남으셨군요. 이런 기본 문제는 백준의 예제가 가끔 불친절한 경우가 있기 때문에 질문 탭에서 test case를 잘 찾아보시길 권합니다.(대부분 제목이 제가 왜 틀렸는지 모르겠어요, 반례가 뭘까요 류입니다. 저도 가끔 왜 틀렸는지 모를 때가 많긴 합니다.)

 

마지막으로 하나의 test case만 남겨두겠습니다.

 

1 1

1

>> 0

 

 

반응형

댓글