https://programmers.co.kr/learn/courses/30/lessons/1829#

 

코딩테스트 연습 - 카카오프렌즈 컬러링북

6 4 [[1, 1, 1, 0], [1, 2, 2, 0], [1, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 3], [0, 0, 0, 3]] [4, 5]

programmers.co.kr

 

 

재귀를 이용한 DFS 방식으로 풀었다.

방문한 픽셀은 color값을 0으로 바꿔주고 방문 시에 해당 픽셀의 color값을 비교해 같은 부분인지 체크하는 방식으로 풀었다. 

 

 

 

#include <iostream>
#include <vector>
using namespace std;

vector<vector<int>> v;
int sizeN, sizeM;
int maxSize = 0;

bool dfs(int x, int y, int color)
{
	if (x <= -1 || x >= sizeN || y <= -1 || y >= sizeM)
	{
		return false;
	}

	if (v[y][x] != 0 && v[y][x] == color)
	{
		v[y][x] = 0;
		maxSize += 1;
		dfs(x - 1, y, color);
		dfs(x, y - 1, color);
		dfs(x + 1, y, color);
		dfs(x, y + 1, color);
		return true;
	}
	return false;
}


vector<int> solution(int m, int n, vector<vector<int>> picture) {
	vector<int> answer(2, 0);
	int number_of_area = 0;
	sizeN = n;
	sizeM = m;
	v = picture;

	for (int i = 0; i < m; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			if (dfs(j, i, picture[i][j]))
			{
				number_of_area += 1;
				answer[1] = maxSize > answer[1] ? maxSize : answer[1];
				maxSize = 0;
			}
		}
	}
	answer[0] = number_of_area;
	return answer;
}

 

 프로그래머스 홈페이지에서 그냥 풀다가 dfs 함수를 if문으로 체크하는 부분에 i와 j를 반대로 줘서 분명 맞는데 왜 값이 다르게 나오지? 하고 디버깅까지 찍어봤다ㅠ

[y][x] 방식으로 이용하는데 함수에 인자로 넘겨줄 때 x,y 순서로 넘겨주고 있어서 문제가 생겼던 것이다... 

 

programmers.co.kr/learn/courses/30/lessons/12913

 

코딩테스트 연습 - 땅따먹기

땅따먹기 게임을 하려고 합니다. 땅따먹기 게임의 땅(land)은 총 N행 4열로 이루어져 있고, 모든 칸에는 점수가 쓰여 있습니다. 1행부터 땅을 밟으며 한 행씩 내려올 때, 각 행의 4칸 중 한 칸만 밟

programmers.co.kr

 

처음에는 단순히 뇌빼기를 하고 풀어도 되는 쉬운 문제인 줄 알고 이전값만 더했는데 테스트에서 실패가 뜨는 것을 보고 직감했다. 이게 아니구나.

그래서 다른 방식으로 풀었다. 

원래는 각 행에 가장 큰 값을 더하되 이전 행에서 더했던 인덱스(열)면 다른 최댓값을 찾았었다.

그런데 생각해보니 반례가 있었다.

1 2 3 4
5 6 8 7
9 10 11 12
1 1 3 1000

 

이렇게 극한으로 치닫는 예제에서 4->8->12를 하고나면 마지막행에서 1000을 더할 수가 없게 된다.

그래서 각 행에 이전 행의 최댓값을 더한 후에 비교해줬다. 

 

 

int find_max(int n1, int n2, int n3)
{
	int temp = n1;

	if (temp < n2)
	{
		temp = n2;
	}
	if (temp < n3)
	{
		temp = n3;
	}
	return temp;
}


int solution(vector<vector<int> > land)
{
	int answer = 0;
	int m_size = land.size();

	for (int i = 0; i < m_size-1; ++i)
	{
		land[i+1][0]+= find_max(land[i][1], land[i][2], land[i][3]);
		land[i+1][1]+= find_max(land[i][0], land[i][2], land[i][3]);
		land[i+1][2]+= find_max(land[i][0], land[i][1], land[i][3]);
		land[i+1][3]+= find_max(land[i][1], land[i][2], land[i][0]);
	}

	answer = land[m_size - 1][0];
	for (int i = 1; i < 4; ++i)
	{
		if (answer < land[m_size - 1][i])
		{
			answer = land[m_size - 1][i];
		}
	}
	return answer;
}

programmers.co.kr/learn/courses/30/lessons/12951

 

코딩테스트 연습 - JadenCase 문자열 만들기

JadenCase란 모든 단어의 첫 문자가 대문자이고, 그 외의 알파벳은 소문자인 문자열입니다. 문자열 s가 주어졌을 때, s를 JadenCase로 바꾼 문자열을 리턴하는 함수, solution을 완성해주세요. 제한 조건

programmers.co.kr

 

처음에는 그냥 공백 구분해서 제일 앞글자만 대문자로 바꿔주면 되는 줄 알았더니 다른 조건들이 있었다.

1. 문자열 중간에 있는 대문자를 소문자로 바꾸기

2. 공백이 길게 있는 경우 그 공백도 answer에 그대로 추가해야 함

 

그러니까 hello    woRld 같은 경우에 Hello WoRld로 출력하면 안되고 Hello    World로 출력해야 한다.

ssstream으로 풀고 테스트 실행했는데 눈이 침침했는지 분명 똑같은 결과인데 뭐가 다르다는 거야; 하면서 한참 고민했다.

웃기네 

 

 

string solution(string s) {
	string answer = "";

	int index = 0;
	bool prev_blank = true;
	while (index < s.size())
	{
		if (prev_blank && s[index] != ' ')
		{
			s[index] = toupper(s[index]);
			prev_blank = false; 
		}
		else if (s[index] == ' ')
		{
			prev_blank = true;
		}
		else if(isupper(s[index]))
		{
			s[index] = tolower(s[index]);
		}

		answer += s[index];
		index += 1;
	}

	return answer;
}

 

 

순열, 조합 구현

C++로 순열과 조합을 구현해보았다.

 

 

 

순열은 STL의 next_permutation과 prev_permutation을 쓰면 쉽게 구현할 수 있긴 하다. 하지만 하다보면 해당 원리를 이용해 조금 변형할 일이 생기기 마련...... 

순열과 조합은 보통 재귀를 이용해 구현한다. 

 

◎ 조합

조합은 순열과 다르게 순서가 없는 나열이다. 그러니까 원소들을 중복 없이 뽑는다.

조합을 먼저 쓴 이유는 조합을 먼저 구해야 순열을 구할 수 있다. 

우선 조합은 nCr로 n개 중에 r개를 뽑는 것이다.

 

만약 배열 {1,2,3,4,5}의 원소 중 3개씩 뽑는 조합을 만든다 했을 때 나올 수 있는 경우의 수는

(1,2,3) (1,2,4) (1,2,5) (1,3,4) (1,3,5) (1,4,5)

(2,3,4) (2,3,5) (2,4,5)

(3,4,5)
로 총 10개가 된다.

 

조합의 공식이 nPr / r! 임을 생각해보면 10개가 맞다. 

 

 

#include <iostream>
using namespace std;


void Combination(int arr[], int n, int r,  int index,int target, int comb_arr[])
{
	if (r == 0)
	{
		// r개를 뽑기로 했는데 r개를 다 뽑은 경우
		for (int i = 0; i < index; ++i)
		{
			cout << comb_arr[i];
		}
		cout << endl;
	}
	else if (target == n)
	{
		// target이 index를 초과=>종료 
		return;
	}
	else
	{
		//target 인덱스의 원소를 저장
		comb_arr[index] = arr[target];
		Combination(arr, n, r - 1, index + 1, target + 1, comb_arr); // 해당 원소를 선택
		Combination(arr, n, r, index, target + 1, comb_arr); // 해당 원소를 선택하지 않음
	}
}

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int n = 5;
	int r = 3;

	int* comb_arr = new int[5];
	
	Combination(arr, 5, 3, 0,0 , comb_arr);
}

 

조합을 하기 위해서 배열을 돌면서 뽑는 경우와 안 뽑는 경우를 조사한다. 

comb_arr는 빈 배열로 Combination 함수를 돌면서 뽑은 원소를 저장하는 배열이다.

 

Combination(arr, n, r - 1, index + 1, target + 1, comb_arr); // 해당 원소를 선택
Combination(arr, n, r, index, target + 1, comb_arr); // 해당 원소를 선택하지 않음

 

왜 두번 호출을 하냐면..

더보기

간단하게 말하면 다음 숫자를 선택하기 위해서라고 말할 수 있다.

먼저 해당 원소를 선택하는 함수를 1번, 선택하지 않는 함수를 2번 함수라고 했을 때 1,2,3과 1,2,4를 예를 들어보면

처음 호출 시 comb_arr[index] = arr[target]에는 index 초기값인 0, target 초기값인 0의 값이 들어가

comb_arr[0]에 1이, 그 다음 1번 함수를 호출하게 되면서 r은 2, index는 1, taget 은 1인 상태로 comb_arr[1]에는 2가 들어가게 된다.

 

그 다음 1번 함수의 호출로 인해 들어간 상태에는

index와 target은 2, r은 1이 된다. 따라서 arr[2]의 값인 3이 comb_arr[2]에 들어가게 되며 

한 번 더 1번 함수를 호출하게 되면 r이 0이므로 123이 출력된다.

여기서 알아둘 것은 재귀함수 호출이므로 아직까지 2번 함수는 한 번도 실행되지 않았다.

출력이 끝난 해당 함수는 종료되고 그때 이제 2번 함수가 호출이 되는데 가장 마지막의 r값인 1의 값을 가지고 있으며 index는 2, target이 2인 상태의 Combination상태가 호출되는 것이다. 

이 상황에서 2번 함수가 호출되면 target의 값만 1 증가하기 때문에 

comb_arr[index] = arr[target]; 줄을 수행하고 나면 comb_arr[2]에는 arr[3]의 값이 들어가게 된다.

그 후 다시 1번 함수를 호출해서 들어간 함수 내부에서 r이 0이 되므로 출력을 하는 것이다. 

 

이 부분을 보면 위의 줄은 target에 위치한 원소를 원소를 뽑는 부분, 밑의 줄은 원소를 선택하지 않는 부분이다.

또한 target은 함수 호출 시 항상 1을 증가시켜서 넘겨야 순회하는 역할을 할 수 있다.

 

선택할 때는 r을 1 감소시키고 index를 1 증가시키며 선택하지 않을 때는 r을 그대로, index도 그대로 넣고 호출한다.

index를 증가시키는 이유는 현재 target이 가리키는 원소를 선택하고 다음 index를 선택하기 위함이며

증가시키지 않는 것은 현재 target이 가리키는 원소를 선택하지 않고 현재 index에 다른 원소를 선택하기 위함이다. 

 

이렇게 순회를 하는 도중, 다 뽑은 경우인 r=0과 r개를 다 뽑았지만 arr의 모든 원소를 다 검사했을 때인 target = n일 때이다. 후자의 경우 실패이므로 아무것도 하지 않고 return하게 된다. 

 

 

 

◎ 순열

순열은 순서가 있는 나열이다. 

우선 n개 중에 n개를 뽑는 순열을 보면 

 

void Swap(int arr[], int num1, int num2)
{
	int temp = arr[num1];
	arr[num1] = arr[num2];
	arr[num2] = temp;
}
void Permutation(int arr[], int index, int arr_size)
{
	// 탈출!
	if (arr_size - 1 == index)
	{
		for (int i = 0; i < arr_size; ++i)
		{
			cout << arr[i];
		}
		cout << endl;
		return;
	}

	for (int i = index; i < arr_size; ++i)
	{
		Swap(arr, index, i);
		Permutation(arr,index + 1, arr_size);
		Swap(arr, index, i);
	}
}


int main()
{
	int arr[5] = { 1,2,3,4,5 };
	Permutation(arr, 0, sizeof(arr) / sizeof(int));
}

 

(1,2,3,4,5) 의 배열에서 5개 중 5개를 뽑는 순열을 구하면 순열 공식에 의해 총 120개가 나오게 된다. 

arr배열 자체를 위에서 구한 조합을 이용해 정렬하는 것이다. 

 

먼저 swap을 이용해서 index번째의 자리 수가 1,2,3,4,일 때를 조사한다. index번째 자리를 선택한 후에 재귀를 통해 그 다음 자리를 선택하다가 arr의 마지막을 가리키면 끝이 난다.

 

재귀 후 다시 swap으로 바꿔준 값을 원상복구를 하면 다시 각 자리 수가 1일 때, 2일 때, 3일 때, 4일 때를 모두 조사할 수 있다. 

 

n개에서 n개를 뽑는 경우를 구했으니 이제 n개 중 r개를 뽑는 경우를 구하면 되는데 

이건 위에서 구한 조합을 이용하면 된다. 먼저 nCr을 이용해 n개 중에 r개를 뽑은 후에 r개의 수로 이루어진 숫자들을 permutation을 이용해서 정렬해주면 된다. 

 

void Swap(int arr[], int num1, int num2)
{
	int temp = arr[num1];
	arr[num1] = arr[num2];
	arr[num2] = temp;
}
void Permutation(int arr[], int index, int arr_size)
{
	// 탈출!
	if (arr_size - 1 == index)
	{
		for (int i = 0; i < arr_size; ++i)
		{
			cout << arr[i];
		}
		cout << endl;
		return;
	}

	for (int i = index; i < arr_size; ++i)
	{
		Swap(arr, index, i);
		Permutation(arr,index + 1, arr_size);
		Swap(arr, index, i);
	}
}
void Combination(int arr[], int n, int r, int index, int target, int comb_arr[])
{
	if (r == 0)
	{
		Permutation(comb_arr, 0, 3);
		cout << endl;
	}
	else if (target == n)
	{
		return;
	}
	else
	{
		comb_arr[index] = arr[target];
		Combination(arr, n, r - 1, index + 1, target + 1, comb_arr); 
		Combination(arr, n, r, index, target + 1, comb_arr); 
	}
}

int main()
{
	int arr[5] = { 1,2,3,4,5 };

	int* comb_arr = new int[5];

	Combination(arr, 5, 3, 0,0 , comb_arr);
}

 

 

혹시 이 포스팅을 보시는 분께..

더보기

순열 함수를 호출하는 부분에서 원래 배열의 '크기' 즉 인자가 몇 개있는지를 구해야 하는데 나는 포인터로 넘겨받고 있어 sizeof를 쓸 수 없고 comb_arr의 사이즈를 구하는 다른 방법을 쓰기 귀찮아서 그냥 3개를 뽑을 거라 위의 코드에는 3을 arr_size로 넘겨줬다.

 

 

 

 

참고 자료;

 

it-earth.tistory.com/53


onsil-thegreenhouse.github.io/programming/algorithm/2018/04/05/permutation_combination/


 

 

 

 

클래스(Class)와 구조체(Struct)의 차이

구조체에 대한 설명과 클래스와 구조체의 차이

 

 

 

연관있는 데이터를 하나로 묶는다는 기본 개념은 동일하다. 그러면 각각은 무엇이고 둘의 차이는? 

 

◎ 구조체

C++에서 구조체를 선언하는 방법

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

 

 

C++에서는 C에서 처럼 typedef를 추가할 필요 없이 사용이 가능하다.

int main()
{
	My_S info = {"hi", 90, 100}
	
	info.name = "hello";
	info.age = 100;
	info.number = 7;
}

 

 

구조체와 클래스를 비교하면서 구조체에는 함수를 추가하지 못한다는 이상한 내용도 가끔 보이는데 아니다.

C++에서는 구조체 안에도 함수 추가 가능하다

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age)
	{
		age = _age;
	}
}

 

여기서 내가 My_S 구조체의 변수를 여러 개 선언해서 메모리 공간에 구조체 변수가 할당이 될 때, 

함수는 모든 구조체 변수 내에 각각 별도로 존재하는 것이 아니다.

구조체 안의 함수는 한 번만 선언된 하나의 함수를 공유한다. 

 

 

그리고 물론 이 함수를 외부로 뺄 수도 있다.

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age);
}

void My_S::SetMyAge(int _age)
{
	age = _age;
}

 

함수 원형 선언은 구조체 안에 두고 정의를 밖으로 빼서 정의하는 것이다.

딱히 쓸 일이 없어 보이지만 구조체를 보는 순간 정의와 기능을 한 눈에 파악하기 쉽게 작성하는 데 도움이 되기도 한다. 

 

그런데 구조체 안에 함수가 정의 되어있을 때와 바깥에 있을 때 다른 점이 하나 존재한다.

바로 구조체 안에 있을 때는 함수가 inline으로 처리되고 있다는 점이다. 

인라인의 의미를 그대로 유지하고 싶다면 inline 키워드를 사용해서 명시적으로 지시하면 된다. 

 

 

 

 

 

◎ 클래스

구조체는 클래스의 일종이다.

struct를 사용하면 구조체, class를 사용하면 클래스가 되는 것이다.

Class My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

위의 구조체와 비교했을 때 기본 선언 시 사용하는 키워드만 다를 뿐이다.

 

 

그런데 이 키워드의 차이는 초기화의 방법의 차이를 발생시킨다.

int main()
{
	My_S info = {"hi", 90, 100};
}

struct는 이와같은 방법으로 초기화가 가능했지만 클래스에서는 불가능하다.

그 이유는 클래스 내에 선언된 함수가 아니고 다른 영역에서 초기화를 하려고 했기 때문이다. 

클래스는 기본적으로 별도의 선언이 없다면 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근이 가능하다. 

 

여기서 접근에 대한 구분이 존재한다. 그것을 접근제어 지시자라고 하는데

public 모두에게 공개; 아무나 접근 가능
protected 상속관계로 얽혀있는 클래스에서만 접근허용
private 클래스 안에서만 건들일 수 있다.

 

접근제어 지시자를 명시하지 않으면 class는 기본으로 private으로 설정한다. 

그래서 위의 class는 private으로 지정되어 있어 외부에서 접근이 불가능한 것이다. 

 

 

이 접근제어 지시자는 구조체에서도 선언이 가능하다.

단, 클래스에서는 따로 지정하지 않으면 private으로 설정이 되지만 구조체에서는 public으로 설정이 된다. 

 

 

 

 

 

 

 

 

 

참고 자료;

윤성우의 열혈 C++ 프로그래밍 ( 저; 윤성우, 출판사; 오렌지 미디어)

'Study > C++ , C#' 카테고리의 다른 글

[C#] 닷넷 API  (0) 2021.12.22
[C++] 전처리기 지시문  (0) 2020.01.19
[C++] QueryPerformanceCounter / 프로그램 실행 시간 측정  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10

 

 

QueryPerformance을 이용해 프로그램 실행 속도 측정

컴퓨터 메인보드의 고해상도 타이머를 이용해 시간 간격을 측정한다.

 

 

 

 

현재 실행 속도를 측정하고 싶으면 QueryPerformanceCounter(=QPC)를 이용하면 된다.

하지만 QPC는 외부 시간 참조와 독립적이며 동기화되지 않으므로 현재 시간값을 구하고 싶으면 GetSystemTimePreciseAsFildTime을 이용하라고 MSDN에 나와있다. 

 

QueryPerformanceFrequency와 QueryPerformanceCounter를 이용하면 타이머, FPS 측정 등 여러 방면으로 활용할 수 있다. 

 

 

 

 

◎ QueryPerformanceFrequency

성능 카운터의 빈도를 검색한다. 

 

BOOL QueryPerformanceFrequency(
  LARGE_INTEGER *lpFrequency
);

 

lpFrequency = 현재 성능 카운터, 타이머의 주파수를 반환한다.

 

 

 

 

 

◎ QueryPerformanceCounter

시간 간격 측정에 사용할 수 있는 고해상도 타임 스탬프인 성능 카운터의 현재 값을 검색한다. 그러니까 현재 CPU의 틱을 받아오는 것이다. 

 

BOOL QueryPerformanceCounter(
  LARGE_INTEGER *lpPerformanceCount
);

 

lpPerformanceCount = 매개변수로 현재 성능 카운터 값을 계수로 받는 변수에 대한 포인터를 넘겨준다. 

 

반환 값은 함수가 성공하면 0 이외의 값을 리턴하고 실패하면 0을 리턴한다. 

(Windows XP 이상에서는 이 기능이 항상 성공해서 0을 리턴하는 경우는 없다.)

 

 

 

 

 

◎ LARGE_INTEGER

여기서 쓰이는 LARGE_INTEGER는 뭘까?

QueryPerformanceCounter나 QueryPerformanceFrequency같은 함수를 사용하기 위해선 크기가 큰 정수형이 필요하다. 

왜? 더 자세한 시간값을 저장하기 위해서 

그래서 windows.h에 포함돼 있는 LARGE_INTEGER가 그와 같은 것이다. 

부호가 있는 64비트 정수형 데이터를 저장하기 위해 선언된 사용자 정의 자료형.

 

LARGE_INTEGER는 구조체인데 그 안을 들여다 보면

typedef union _LARGE_INTEGER {
  struct {
    DWORD LowPart;	// 32bit 정수형
    LONG  HighPart;	// 32비트 정수형
  } DUMMYSTRUCTNAME;
  struct {
    DWORD LowPart;
    LONG  HighPart;
  } u;
  LONGLONG QuadPart;	// 64비트 정수형
} LARGE_INTEGER;

이렇게 되어 있다. 

컴파일러가 64비트를 지원할 땐 64비트 정수형 변수에, 32비트 지원 시에는 32비트 정수형 변수에 64비트 측정값을 나눠 저장한다. 

 

값은 64비트의 부호있는 정수형인 QuardPart에 저장되는 것이며 

LowPart는 하위 32비트 DWORD형, HighPart는 상위 32비트 LONG 형이다.

 

64비트 중 LowPart(32bit)와 HighPart(32bit)를 둘다 사용함으로써 더 큰값을 사용할 수 있다. 

 

 

 

 

 

◎ 사용법

#include <windows.h>

int main()
{
	LARGE_INTEGER timer, start, end;
	float DeltaTime;
    
	QueryPerformanceFrequency(&timer); // 타이머의 주파수를 얻어온다. 
   
   
	QueryPerformanceCounter(&start);  // 시작 시점의 CPU 클럭 수
    
    
    // 실행할 내용
    
    
	QueryPerformanceCounter(&end);	// 종료 시점의 CU 클럭 수
    
	DeltaTime = (end.QuadPart - start.QuadPart) / (float)timer.QuadPart; 	// 걸린 시간 계산
 
 }

 

 

 

 

 

 

참고 자료;

 

https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency

 

QueryPerformanceFrequency function - Win32 apps

Retrieves the frequency of the performance counter.

docs.microsoft.com

 


https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

 

QueryPerformanceCounter function - Win32 apps

Retrieves the current value of the performance counter, which is a high resolution (<1us) time stamp that can be used for time-interval measurements.

docs.microsoft.com

 

 

'Study > C++ , C#' 카테고리의 다른 글

[C++] 전처리기 지시문  (0) 2020.01.19
[C++] 클래스(Class)와 구조체(Struct)의 차이  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10
[C++] 참조자와 함수  (0) 2020.01.08

 

https://programmers.co.kr/learn/courses/30/lessons/12980

 

코딩테스트 연습 - 점프와 순간 이동 | 프로그래머스

OO 연구소는 한 번에 K 칸을 앞으로 점프하거나, (현재까지 온 거리) x 2 에 해당하는 위치로 순간이동을 할 수 있는 특수한 기능을 가진 아이언 슈트를 개발하여 판매하고 있습니다. 이 아이언 슈트는 건전지로 작동되는데, 순간이동을 하면 건전지 사용량이 줄지 않지만, 앞으로 K 칸을 점프하면 K 만큼의 건전지 사용량이 듭니다. 그러므로 아이언 슈트를 착용하고 이동할 때는 순간 이동을 하는 것이 더 효율적입니다. 아이언 슈트 구매자는 아이언 슈트를 착용

programmers.co.kr

 

 

 

 

 

더보기

OO 연구소는 한 번에 K 칸을 앞으로 점프하거나, (현재까지 온 거리) x 2 에 해당하는 위치로 순간이동을 할 수 있는 특수한 기능을 가진 아이언 슈트를 개발하여 판매하고 있습니다. 이 아이언 슈트는 건전지로 작동되는데, 순간이동을 하면 건전지 사용량이 줄지 않지만, 앞으로 K 칸을 점프하면 K 만큼의 건전지 사용량이 듭니다. 그러므로 아이언 슈트를 착용하고 이동할 때는 순간 이동을 하는 것이 더 효율적입니다. 아이언 슈트 구매자는 아이언 슈트를 착용하고 거리가 N 만큼 떨어져 있는 장소로 가려고 합니다. 단, 건전지 사용량을 줄이기 위해 점프로 이동하는 것은 최소로 하려고 합니다. 아이언 슈트 구매자가 이동하려는 거리 N이 주어졌을 때, 사용해야 하는 건전지 사용량의 최솟값을 return하는 solution 함수를 만들어 주세요.

예를 들어 거리가 5만큼 떨어져 있는 장소로 가려고 합니다.
아이언 슈트를 입고 거리가 5만큼 떨어져 있는 장소로 갈 수 있는 경우의 수는 여러 가지입니다.

  • 처음 위치 0 에서 5 칸을 앞으로 점프하면 바로 도착하지만, 건전지 사용량이 5 만큼 듭니다.
  • 처음 위치 0 에서 2 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 2) x 2에 해당하는 위치로 이동할 수 있으므로 위치 4로 이동합니다. 이때 1 칸을 앞으로 점프하면 도착하므로 건전지 사용량이 3 만큼 듭니다.
  • 처음 위치 0 에서 1 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 1) x 2에 해당하는 위치로 이동할 수 있으므로 위치 2로 이동됩니다. 이때 다시 순간이동 하면 (현재까지 온 거리 : 2) x 2 만큼 이동할 수 있으므로 위치 4로 이동합니다. 이때 1 칸을 앞으로 점프하면 도착하므로 건전지 사용량이 2 만큼 듭니다.

위의 3가지 경우 거리가 5만큼 떨어져 있는 장소로 가기 위해서 3번째 경우가 건전지 사용량이 가장 적으므로 답은 2가 됩니다.

 

 

 

입출력 예

 

N Result
5 2
6 2
5000 5

입출력 예 설명

입출력 예 #1
위의 예시와 같습니다.

 

입출력 예 #2
처음 위치 0 에서 1 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 1) x 2에 해당하는 위치로 이동할 수 있으므로 위치 2로 이동합니다. 이때 1 칸을 앞으로 점프하면 위치3으로 이동합니다. 이때 다시 순간이동 하면 (현재까지 온 거리 : 3) x 2 이동할 수 있으므로 위치 6에 도착합니다. 이 경우가 건전지 사용량이 가장 적으므로 2를 반환해주면 됩니다.

 

입출력 예 #3
위와 같은 방식으로 합니다.

 

 

#include <iostream>
using namespace std;

int solution(int n)
{
	int ans = 0;
	int remain_meter = n;
	for (int i = remain_meter; i > 0; i /= 2) {
		if (i % 2 == 1) {
			ans += 1;
		}
	}
	return ans;
}

 

 

 

 

 

매크로 함수와 인라인(Inline) 함수

in은 '내부', line은 '프로그램 코드라인'을 의미한다.

즉 프로그램 코드라인 안으로 들어가 버린 함수라는 뜻이다.

매크로, 일반 함수, 인라인 함수 모두 결과는 동일하지만 결과를 위한 과정이나 성격은 상당히 다르다! 

 

 

 

 

◎ 매크로 함수

#define 선행 처리 지시문에 인수로 함수의 정의를 전달해서 함수처럼 동작하는 매크로를 만드는 것이다. 

물론 일반 함수와는 달리 단순 치환만을 해주므로, 일반 함수와 완전히 똑같은 방식으로 동작하지는 않는다. 

매크로 함수는 일반적인 함수에 비해 실행 속도에서 이점이 존재하지만 가독성도 떨어질 수 있고 디버깅도 어려우며 복잡한 함수를 매크로의 형태로 정의하는 것에 한계가 존재한다. 

빠른 이유?

더보기

일반 함수처럼 호출된 함수를 위한 스택 메모리의 할당을 할 필요도, 실행위치의 이동과 매개변수로의 인자를 전달할 필요도, return 문에 의한 값의 반환을 할 필요도 없기 때문이다. 

 

 

매크로 함수의 예시.

#define Add(x) ((x)+(x))

int main()
{
	int a = Add(3);
	cout << a << endl;
}

이렇게 실행하면 a의 출력값은 6이 나올 것이다. 

 

 

왜 '전처리 과정'을 거칠까?

더보기

#와 ## 연산자는 선행처리기 연산자이다. 

이 선행처리기 연산자는 전처리기-> 어셈블러로 넘어가기 전에 전처리기가 # 뒤에 붙은 코드들을 처리하기 때문이다. 그래서 전처리 과정에서 처리한다. 

 

 

그리고 이 과정은 

#define Add(x) ((x)+(x))

int main()
{
	int a = Add(3);
	cout << ((3)+(3)) << endl;
}

이런 과정을 거치게 되는데 위와 같이 함수의 몸체 부분이 함수 호출 문장을 완전히 대체했을 때 '함수가 인라인화 됐다'고 표현한다. 

 

 

 

 

◎ 인라인 함수

인라인 함수는 매크로 함수의 장점은 유지하고 단점은 제거했다고 볼 수 있다.

 

또한 인라인 함수는 inline 예약어를 빼면 기존 함수와 다를 바 없고 자료형 등이 지정돼 있어 문법적으로도 완벽한 함수이다. 

매크로를 이용한 함수의 인라인화는 전처리기에 의해 처리되지만 키워드 inline을 이용한 함수의 인라인화는 컴파일러에 의해 처리된다. 

 

inlineint Add(int x)
{
	return x+x;
 }

int main()
{
	int a = Add(3);
	cout << a << endl;
}

 

그러나 매크로 함수의 장점을 인라인 함수가 완벽하게 가져오지 못한 부분도 있다.

예를 들면 위의 매크로 함수의 예시에서 x는 자료형에 의존적이지 않다. 

즉 실수를 넣든, 정수를 넣든 데이터 손실이 발생하지 않는다.

3을 넣으면 6이 1.92를 넣으면 3.84가 나오게 된다. 

하지만 인라인 함수는 자료형 기반으로 정의된 함수이기 때문에 int 자료형 기반에서 1.92를 넣게 되면 0.84의 손실이 발생한다. 

 

물론 함수의 오버로딩을 통해 문제 해결이 가능하지만 간단하게 한 번만 정의하면 된다는 매크로 함수의 장점과는 멀어지게 된다.

그런데 C++에는 이에 대한 해결책이 또 하나 존재한다. 바로 템플릿을 이용하는 것이다. 템플릿을 이용하면 여러 번 정의하지 않아도 한 번에 해결된다!

 

 

 

 

 

◎ 추가

Visual Studio를 통해 컴파일할 경우, 인라인으로 선언하지 않았더라도 적합하면 인라인 함수로 확장하는 설정이 있다.

반대로 인라인으로 선언했다고 해도 적합하지 않다면 일반 함수로 컴파일한다. 

 

기본값은 '인라인 함수로 사용하는 데 적합한 함수는 모두 인라인 함수로 만든다'는 의미이다. 

설정값은 보통 '기본값'으로 지정돼 있다.

 

 

 

 

 

 

 

참고 출처;


http://tcpschool.com/c/c_prepro_macroFunc  (코딩의 시작, TCP School ; 매크로 함수)

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com


윤성우의 열혈 C 프로그래밍 ( 저; 윤성우, 출판사 ; 오레지 미디어)


이것이 C++이다 ( 저; 최호성, 출판사 ; 한빛미디어 )

 

 

 

 

+ Recent posts