문제 : https://www.acmicpc.net/problem/1715


[알고리즘풀이]

현재 카드 N 묶음 중 가장 작은 2개를 합치고, 다시 N - 1 묶음 중 가장 작은 2개를 합치는 방식으로 진행했을 때, 최솟값이 나옴을 알 수 있다. 매번 sort를 진행해 가장 작은 2개를 찾게 된다면, 시간 초과를 마주할 것이다.

따라서, 우선순위 큐를 활용해서 문제를 해결해야 한다.

#include<iostream>
#include<queue>

using namespace std;

long long sum = 0;
priority_queue<int, vector<int>, greater<int>> q;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int temp;
		cin >> temp;
		q.push(temp);
	}
	while (q.size() != 1) {
		int temp;
		temp = q.top();
		q.pop();
		temp += q.top();
		q.pop();
		sum += temp;
		q.push(temp);
	}
	cout << sum;
}

 

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 9421 - Happy Prime Number  (0) 2019.10.03
[BOJ] 1456 - 거의 소수  (0) 2019.10.03
[BOJ] 2225 - 합분해  (0) 2019.10.01
[BOJ] 1914 - 하노이 탑  (0) 2019.10.01
[BOJ] 2003 - 수들의 합  (0) 2019.09.30

문제 : https://www.acmicpc.net/problem/2225


[알고리즘풀이]

Dp[i][j] : 숫자 j 를 i 개의 숫자로 표현할 수 있는 경우의 수.

그럼 다음과 같은 방정식이 성립한다.

Dp[i][j] = Dp[1][j - 1] + Dp[2][j - 1] +  + Dp[i][j-1]

#include<iostream>

using namespace std;

#define m 1000000000;
int dp[201][201];

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int n, k;
	cin >> n >> k;
	for (int j = 0; j <= n; j++)
		dp[1][j] = 1;
	for (int j = 0; j <= k; j++)
		dp[j][0] = 1;

	for(int i = 2; i <= k; i++){
		for (int j = 1; j <= n; j++) {
			int temp = 0;
			for (int k = 1; k <= i; k++)
				temp = (temp +  dp[k][j - 1]) % m;
			dp[i][j] = temp % m;
		}
	}
	cout << dp[k][n];
}

 

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 1456 - 거의 소수  (0) 2019.10.03
[BOJ] 1715 - 카드 정렬하기  (0) 2019.10.02
[BOJ] 1914 - 하노이 탑  (0) 2019.10.01
[BOJ] 2003 - 수들의 합  (0) 2019.09.30
[BOJ] 10819 - 차이를 최대로  (0) 2019.09.30

문제 : https://www.acmicpc.net/problem/1914


[알고리즘풀이]

하노이 탑의 이동 횟수를 계산하는 함수와, 이동 과정을 보여주는 함수로 나눠서 해결할 수 있습니다.

[1]

하노이 탑의 이동 횟수는 H[n] = 2 * H[n-1] + 1 점화식을 이용해서 구하면 되는데,

N이 최대 100으로 N이 커지면 long long 의 범위로도 답을 담을 수 없습니다.

따라서, string을 이용해 long long 범위를 벗어나는 덧셈을 해결할 수 있었습니다.

[2]

이동 과정을 보여주는 함수는 / 얼마만큼의 원판을 / 어디서 / 어디를 이용해 / 어디로 보낼지 / 4개의 값을 이용해

재귀적으로 쉽게 구현할 수 있습니다.

#include<iostream>

using namespace std;

string h[101] = { "0", "1", };

string string_sum(string a, string b) {
	string answer ="";
	int up = 0;
	for (int i = a.length() - 1; i >= 0; i--) {
		if (i == a.length() - 1) {
			int temp = (a[i] - '0') + (b[i] - '0') + 1;
			up = temp / 10;
			temp -= 10 * up;
			answer += (char)(temp + '0');
		}
		else{
			int temp = (a[i] - '0') + (b[i] - '0') + up;
			up = temp / 10;
			temp -= 10 * up;
			answer += (char)(temp + '0');
		}
	}
	if (up != 0)
		answer += (char)(up + '0');
	string ret="";
	for (int i = answer.length() - 1; i >= 0; i--)
		ret += (char)answer[i];
	return ret;
}

void hanoi(int n, char a, char b, char c) {
	if (n == 1) {
		cout << a << ' ' << c << '\n';
		return;
	}
	hanoi(n - 1, a, c, b);
	hanoi(1, a, b, c);
	hanoi(n - 1, b, a, c);
	return;
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	for (int i = 2; i <= n; i++)
		h[i] = string_sum(h[i - 1], h[i - 1]);
	cout << h[n] << '\n';
	if (n > 20)
		return 0;
	hanoi(n, '1', '2', '3');
	return 0;
}

 

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 1715 - 카드 정렬하기  (0) 2019.10.02
[BOJ] 2225 - 합분해  (0) 2019.10.01
[BOJ] 2003 - 수들의 합  (0) 2019.09.30
[BOJ] 10819 - 차이를 최대로  (0) 2019.09.30
[BOJ] 1520 - 내리막 길  (0) 2019.09.29

안녕하세요, 트레블비입니다.

오늘은 빠르게 어떤 수의 N 제곱 구하기에 대해서 포스팅하겠습니다.


[O(N) 으로 구하기]

 

예를 들어, 2의 100 제곱을 구한다고 해봅시다. ( Int 형 범위는 고려하지 않겠습니다. )

2를 계속 100번 곱하면 간단하게 답을 얻을 수 있겠죠? 

int answer = 1;
for(int i = 0; i < 100; i++)
	answer = answer * 2;
//주의 : 위의 코드는 Int 형 범위를 넘어가므로 제대로 작동하지 않습니다! 예시일 뿐입니다. 

 

그렇다면, 2의 100,000,000,000(1000억) 제곱을 구한다고 한다면 2를 계속 곱해가는 방법으로 빠르게 풀 수 있을까요??

int answer 1;
for(int i = 0; i < 100000000000; i++)
	answer = answer * 2;
//주의 : 위의 코드는 Int 형 범위를 넘어가므로 제대로 작동하지 않습니다! 예시일 뿐입니다.

당연히 1000억 번의 연산이 필요하므로 시간이 엄청 오래 걸립니다.

어떤 수 A의 N제곱을 구해야 되는데 N이 굉장히 크다면,  위의 방법은 시간 복잡도가 O(N)으로 사용할 수 없습니다.

 

[O(logN) 으로 구하기]

 

똑같이 2의 100 제곱을 구한다고 해봅시다. ( Int 형 범위는 고려하지 않겠습니다. )

100은 64 + 32 + 4 로 표현할 수 있습니다. 즉, 100 = 1100100(2) 입니다.

당연히, 모든 수는 2진수로 표현이 가능합니다!

이 사실을 이용해 훨씬 더 빠르게 2의 100 제곱을 구해보겠습니다.

int answer = 1, A = 2, N = 100;
while(N){
  if (N & 1)
  	answer = answer * A;
  N = N / 2;
  A = A * A;
}
//주의 : 위의 코드는 Int 형 범위를 넘어가므로 제대로 작동하지 않습니다! 예시일 뿐입니다.

 

A = 2, N은 100 = 1100100(2) 이고 마찬가지로 2^100인 A^N을 구하는 코드입니다.

 

먼저, 코드를 살펴보겠습니다.

N = 100 = 1100100(2) 이고, A는 2, Answer = 1로 시작합니다.

N & 1 은 N의 마지막 비트와 1을 And연산을 취하는 것으로, N & 1 은 False입니다.

따라서 answer는 그대로 1이고, N = N / 2 이므로 N = 50 = 110010(2)로 마지막 비트가 사라지고,

A = A * A ; 로 2^2인 4가 됩니다.

 

이 과정을 반복하면 아래 표와 같이 작동합니다.

단계

Answer = 1

A = 2

N = 1100100(2)

1

1

2^2

110010(2)

2

1

2^4

11001(2)

3

2^4

2^8

1100(2)

4

2^4

2^16

110(2)

5

2^4

2^32

11(2)

6

2^4 * 2^32 = 2^36

2^64

1(2)

7

2^4 * 2^32 * 2^64 = 2^100

2^128

0(2)

#주의 : 위의 코드는 Int 형 범위를 넘어가므로 제대로 작동하지 않습니다! 예시일 뿐입니다.

 

100번의 연산이 아니라, 각 단계마다 3번의 연산으로 무려 7단계 만에 답을 구할 수 있습니다.

N이 커지면 커질수록 처음의 알고리즘보다 훨씬 더 빠르게 작동하겠죠?

잘 익혀두시면 정말 유용하게 사용하는 알고리즘입니다!

 

함수로 표현하면 다음과 같습니다.

long long math_pow(long long x, long long y)
{
	long long tmp = 1;
	while (y)
	{
		if (y & 1)
			tmp = tmp * x;
		y = y / 2;
		x = x * x;
	}
	return tmp;
}

당연히, A^N이 Int 나 long long 형 범위를 넘어가는 문제들은 A^N 을 어떤 수로 나눈 나머지를 구해달라 합니다!

아래와 같이 함수로 표현할 수 있습니다.

long long math_pow(long long x, long long y)
{
	long long tmp = 1;
	while (y)
	{
		if (y & 1)
			tmp = tmp * x % m;
		y = y / 2;
		x = x * x % m;
	}
	return tmp;
}

오늘의 포스팅은 여기서 마치겠습니다.

[추천문제]
https://www.acmicpc.net/problem/1629

 

+ Recent posts