전체 글 (10)

10
31

1. iota(first, last, value)

first 부터 last 전까지를 value 부터 시작하는 연속적인 값으로 채워줌

#include <string>
#include <vector>
#include <numeric>

using namespace std;

//answer 벡터에 start 부터 end 까지 +1씩 증가하는 수로 채워준다
vector<int> solution(int start, int end) {
    vector<int> answer(end - start + 1);
    iota(answer.begin(), answer.end(), start);
    return answer;
}

2. accumulate(first, last, initvalue)

초기값 ininvalue + first 부터 last 전까지의 합을 반환 (init은 초기값)

#include <string>
#include <vector>
#include <numeric>

using namespace std;

//arr 원소의 총 합 출력
int solution(vector<int> arr) {
    int sum = accumulate(arr.begin(),arr.end(),0);
    return sum;
}

3.inner_product(first1, last1, first2, init)

first부터 last 전 까지의 값과 first2로 시작하는 값에 대해 내적 연산을 수행한다. (init은 초기값)

#include <string>
#include <vector>
#include <numeric>

using namespace std;

//arr1 = {1,2,3} , arr2 = {4,5,6} 일때 1*4 + 2*5 + 3*6 출력
int solution(vector<int> arr1, vector<int> arr2) {
    int sum = inner_product(arr1.begin(),arr1.end(),arr2.begin(),0);
    return sum;
}

4. gcd(value1, value2)

value1과 value2의 최대공약수 반환. (최소공배수는 lcm(value1, value2))

#include <string>
#include <vector>
#include <numeric>

using namespace std;

vector<int> solution(int n, int m) {
    vector<int> answer;
    
    answer.push_back(gcd(n,m)); //n과 m의 최대공약수
    answer.push_back(lcm(n,m)); //n과 m의 최소공배수
    
    return answer;
}

'C++ > 자료노트' 카테고리의 다른 글

기타 유용한 함수 등등  (0) 2023.08.08
<vector>  (0) 2023.07.30
<algorithm>  (0) 2023.07.30
<string>  (0) 2023.07.30
COMMENT
 
10
14

1. 점프와 순간 이동

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

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

위 문제를 풀면서, 나는 input으로 받은 수를 계속 2로 나누며 나머지가 생기는 횟수를 카운트했다.

그런데 다른 사람 풀이에서 __builtin_popcount 함수를 이용해 코드 한 줄로 문제를 해결할 수 있다는 것을 알게 되었다.

#include <iostream>
using namespace std;

int solution(int n) {
    return __builtin_popcount(n);
}

__builtin_popcount(n)란 입력으로 받은 수를 이진수로 표현했을 때, 해당 수가 가진 1의 수를 세어 주는 함수이다.

예를 들어 n이 13이라면, 이진수로 1101이므로 3을 반환하는 방식이다.

이때 2로 나눈다는 것은 해당 이진수를 오른쪽으로 1회 시프트 연산을 하는 것과 마찬가지이고, 그 때의 마지막 자리수가 1이라면 해당 숫자는 홀수이므로 2로 나눴을 때 나머지가 생기는 상황과 동일하다.

 

 

 

해당 함수를 알게 된 이후로 종종 비슷한 문제를 풀게 될 때마다 잘 사용하고 있어서, 예시를 아래 정리해 둔다.

(참고: visual studio는 프로그래머스(gcc)와 다른 환경(msvc)을 사용하기 때문에 __popcnt 함수를 사용한다고 한다)

 

 

2. 다음 큰 숫자

https://school.programmers.co.kr/learn/courses/30/lessons/12911

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

이 문제는 아예 문제에서부터 이진수로 변환했을 때의 1의 개수를 언급한다.

주어진 숫자의 이진수와 1의 개수가 같으면서 다음으로 큰 숫자를 구하는 문제이다.

#include <string>
#include <vector>

using namespace std;

int solution(int n) {
    int answer = n+1; //answer는 n보다 무조건 크기 때문에 +1
    
    while(1)
    {
    	//주어진 숫자 n과 answer의 이진수의 1의 개수가 같아질 때까지 answer에 1을 더한다
    	//처음으로 이진수의 1의 개수가 같아진 순간이 정답이다
        if(__builtin_popcount(answer) == __builtin_popcount(n))
            break;
        answer++;
    }
    
    return answer;
}

 

 

 

3. 배열의 길이를 2의 거듭제곱으로 만들기

https://school.programmers.co.kr/learn/courses/30/lessons/181857

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

주어진 vector의 길이가 2의 정수 거듭제곱이 될 때까지 뒤에 0을 추가하여 출력하는 문제이다.

2의 제곱수는 가장 큰 자리수 하나만 1일 때임을 이용한다. (4 = 0010 (2), 32 = 0010 0000 (2) 등)

#include <vector>

using namespace std;

vector<int> solution(vector<int> arr) {  
	//arr 사이즈의 이진수의 1의 개수가 1이 될 때까지 0을 추가한다
    while(__builtin_popcount(arr.size()) != 1)
        arr.push_back(0);
    
    return arr;
}

 

 

 

4. 막대기

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

 

1094번: 막대기

지민이는 길이가 64cm인 막대를 가지고 있다. 어느 날, 그는 길이가 Xcm인 막대가 가지고 싶어졌다. 지민이는 원래 가지고 있던 막대를 더 작은 막대로 자른다음에, 풀로 붙여서 길이가 Xcm인 막대

www.acmicpc.net

2의n제곱들을 하나씩만 더해 특정 수를 만드는 문제이므로 점프와 순간이동과 같은 원리이다.

#include <iostream>

using namespace std;

int main()
{
    int num;
    cin >> num;
    
    cout << __builtin_popcount(num);
    
    return 0;
}

 

 

 

5.  물병

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

 

1052번: 물병

지민이는 N개의 물병을 가지고 있다. 각 물병에는 물을 무한대로 부을 수 있다. 처음에 모든 물병에는 물이 1리터씩 들어있다. 지민이는 이 물병을 또 다른 장소로 옮기려고 한다. 지민이는 한 번

www.acmicpc.net

가장 적은 수로 만든 물병의 갯수가 곧 이진수로 표현했을 때 1의 갯수와 같기 때문에 1씩 더하면서 조건값보다 1의 갯수가 작아질 때까지 반복하면 된다.

#include <iostream>

using namespace std;

int main()
{
    int N, K;
    cin >> N >> K;

    int answer = 0;

    while (__builtin_popcount(N) > K)
    {
        answer++;
        N++;
    }

    cout << answer;

    return 0;
}

'C++ > 코딩테스트' 카테고리의 다른 글

가운데를 말해요 (백준1655)  (0) 2023.08.17
기차운행 Stack 응용  (0) 2023.07.21
영지 선택 Territory large  (0) 2023.07.13
어글리 넘버 Ugly Numbers  (1) 2023.07.11
COMMENT
 
08
17

문제

백준이는 동생에게 "가운데를 말해요" 게임을 가르쳐주고 있다. 백준이가 정수를 하나씩 외칠때마다 동생은 지금까지 백준이가 말한 수 중에서 중간값을 말해야 한다. 만약, 그동안 백준이가 외친 수의 개수가 짝수개라면 중간에 있는 두 수 중에서 작은 수를 말해야 한다.

예를 들어 백준이가 동생에게 1, 5, 2, 10, -99, 7, 5를 순서대로 외쳤다고 하면, 동생은 1, 1, 2, 2, 2, 2, 5를 차례대로 말해야 한다. 백준이가 외치는 수가 주어졌을 때, 동생이 말해야 하는 수를 구하는 프로그램을 작성하시오.

첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -10,000보다 크거나 같고, 10,000보다 작거나 같다.

 

해결

코딩 테스트를 위한 자료 구조와 알고리즘 with C++ 이라는 책의 예제에서 만난 문제인데 어쩐지 코테 문제일 것 같더라니 역시나 백준에 비슷한 문제가 있어서 책 풀이를 정리해보았다.

단순히 정렬 후 가운데 값을 인덱스로 가져와도 동작은 하지만 시간 초과가 나버린다.

이 문제는 우선순위 큐 두 개를 사용하면 해결할 수 있다.

우선 maxHeap(root노드, 즉 [0]에 항상 최대값이 들어있는 힙)과 minHeap([0]에 항상 최소값이 들어있는 힙)을 생성한다.

maxHeap에는 중앙값보다 작은 수들이, minHeap에는 중앙값보다 큰 수들이 들어갈 예정으로, 중앙값은 maxHeap의 루트노드(중앙값보다 작은 값들 중에 가장 큰 값) 또는 minHeap의 루트 노트(중앙값보다 큰 값들 중에 가장 작은 값)가 될 것이다.

그리고 첫 번째 입력은 maxHeap에 넣는다.

현재 중앙값은 1이다.

두 번째 입력부터는 각 힙의 크기와 현재 중앙값을 고려하여 어느 힙에 넣을 것인지를 판단해야한다.

우선 minHeap이 maxHeap보다 작으므로 minHeap에 넣는 것을 먼저 고려해야 한다.

현재 중앙값은 1이다.(전체 수가 짝수개일 경우 둘 중 작은 것을 중앙값으로 하기 때문에)

이제 maxHeap과 minHeap의 크기가 동일하다.

따라서 다음 입력은 넣고자 하는 값과 현재 중앙값을 비교해서 입력해야한다.

넣고자 하는 값이 현재 중앙값보다 크면 minHeap에, 아니라면 maxHeap에 넣는다.

넣어진 값은 자동으로 힙 정렬이 된다.

현재 중앙값은 2이다.

이제 다시 maxHeap이 minHeap보다 사이즈가 작으므로 다음 입력을 maxHeap에 넣는 것을 고려해야 한다.

하지만 무조건적으로 수가 적은 Heap에 데이터를 넣으면 다음과 같은 문제가 생긴다.

maxHeap에 넣은 10이 힙 정렬에 의해 루트 노드가 되었는데, 이 루트 노드는 현재 중앙값인 minHeap의 루트 노드보다 큰 상태라서, maxHeap에는 중앙값보다 작은 수가 들어있어야 한다는 규칙을 위반한다.

maxHeap이 중앙값(2) 보다 큰 수 10을 들고 있다 -> 오류!

따라서 두 힙 간의 사이즈 차이가 있을 경우, 넣고자 하는 데이터와 현재 중앙값을 비교하여 데이터를 주고받는 과정이 필요하다.

예를 들어 maxHeap의 수가 적어 데이터를 넣고자 하는데 이 데이터가 중앙값(현재 minHeap의 루트 노드)보다 커서 규칙이 깨질 경우, 우선 minHeap의 루트 노드 데이터를 maxHeap에 넘긴다.

해당 값을 받은 maxHeap은 힙 정렬되어 해당 값이 루트 노드가 되도록 정렬할 것이다.

minHeap 또한 넘긴 데이터를 제외하고 다시 데이터를 정렬할 것이다.

이제 maxHeap보다 minHeap의 사이즈가 작으므로 minHeap에 넣고자 했던 데이터를 삽입한다.

minHeap이 힙 정렬을 하고 나면 다음과 같은 상태가 된다.

현재 중앙값은 2이다.

이제 다시 minHeap과 maxHeap의 사이즈가 같아졌다.

다음 입력은 아까와 마찬가지로 현재 중앙값보다 값이 크면 minHeap에, 값이 작으면 maxHeap에 넣는다.

현재 중앙값은 2이다.

minHeap이 maxHeap보다 사이즈가 작다.

이때 넣고자 하는 데이터 값 7이 현재 중앙값보다 크므로 minHeap에 넣어도 규칙이 깨지지 않는다.

(Heap은 인덱스 순서대로 정렬하지 않기 때문에 [0] 이후의 데이터들이 숫자 크기 순이 아닐 수 있지만, 루트 노드의 성질은 항상 성립한다)

현재 중앙값은 2이다.

이제 maxHeap과 minHeap의 사이즈가 같으므로 중앙값과 데이터를 비교하여 넣는다.

처음 코드가 실행될 때 maxHeap부터 입력했으므로 중앙값과 데이터가 같을 때는 maxHeap에 넣도록 한다.

최종 중앙값은 5이다.

이런 방식을 어떻게 생각해낸건지 처음 생각한 사람은 천재임이 분명하다.

 

코드 구현

#include <iostream>
#include <queue>
#include <vector>

struct median
{
	//최대 힙과 최소 힙을 생성한다
	std::priority_queue<int> maxHeap;
	std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

	void insert(int data)
	{
    	//처음 시작할 때는 maxHeap에 데이터를 넣는다
		if (maxHeap.size() == 0)
		{
			maxHeap.push(data);
			return;
		}
		
        //두 Heap의 사이즈가 같을 경우 입력하고자 하는 데이터와 중앙값을 비교하여
        //데이터가 중앙값보다 크다면 minHeap에, 아니라면 maxHeap에 넣는다
		if (maxHeap.size() == minHeap.size())
		{
			if (data <= get())
				maxHeap.push(data);
			else
				minHeap.push(data);

			return;
		}

		//두 힙 간의 사이즈 차이가 날 경우 데이터와 중앙값을 비교한다
        //maxHeap에 데이터를 넣으려는데 그 데이터가 중앙값(minHeap의 루트 노드)보다 클 경우
        //중앙값을 maxHeap에 옮겨준 후에 minHeap에 새 데이터를 넣는다.
		if (maxHeap.size() < minHeap.size())
		{
			if (data > get())
			{
				maxHeap.push(minHeap.top());
				minHeap.pop();
				minHeap.push(data);
			}
			else
				maxHeap.push(data);

			return;
		}

		//minHeap에 데이터를 넣으려는데 그 데이터가 중앙값(maxHeap의 루트 노드)보다 작을 경우
        //중앙값을 minHeap에 옮겨준 후에 maxHeap에 새 데이터를 넣는다.
		if (data < get())
		{
			minHeap.push(maxHeap.top());
			maxHeap.pop();
			maxHeap.push(data);
		}
		else
			minHeap.push(data);
	}

	//중앙값을 구한다
    //만약 중앙값이 두 개라면 둘 중 작은 값을 중앙값으로 한다
	int get()
	{
		if (maxHeap.size() == minHeap.size())
			return maxHeap.top() > minHeap.top() ? minHeap.top() : maxHeap.top();

		if (maxHeap.size() < minHeap.size())
			return minHeap.top();

		return maxHeap.top();
	}
};

int main()
{
   std::cin.tie(NULL);
    std::ios::sync_with_stdio(false);
    
    int nNum;
    std::cin >>nNum;
    
	median med;
    
    for(int i = 0; i < nNum ; i++)
    {
        int num;
        std::cin >> num;
        med.insert(num);
        std::cout << med.get() << '\n';
    }
    
	return 0;
}

 

테스트

테스트 결과는 백준 채점 결과로 대신한다.

푼 사람들 중에서도 상당히 빠른 결과인 것 같다.

'C++ > 코딩테스트' 카테고리의 다른 글

__builtin_popcount 관련 문제  (0) 2023.10.14
기차운행 Stack 응용  (0) 2023.07.21
영지 선택 Territory large  (0) 2023.07.13
어글리 넘버 Ugly Numbers  (1) 2023.07.11
COMMENT
 
1 2 3 4