no image
[백준-10943] 팰린드롬?
( "팰린드롬 "이란 뒤집어도 똑같은 수를 의미한다. ) 일단 시간 제한이 0.5초(5천만줄) 정도이고, 입력의 크기가 2000 개의 수열의 크기, 백만개의 질문의 개수이다. 이를 보아 질문마다 2000 번의 연산을 하게 된다면 문제가 바로 터진다는 걸 알 수 있다. 때문에 백만개의 질문마다 log(n) 의 연산을 하던가 아예 처음에 다 계산한 이후 O(1) 의 복잡도로 답을 내버리던가 해야 한다. 팰린드롬은 대표적인 dp 문제라고 할 수 있다. 예를 들어 1, 2, 1, 3, 1, 2, 1 위와 같이 7개의 수가 있을 때 (3, 7) 이 팰린드롬인지 확인해보자. (3,7) 은 1, 3, 1, 2, 1 로 양끝의 수가 같은 팰린드롬 같은(?) 수이다. 이 때 양끝의 수가 같으니 (3, 7) = (4, 6..
2021.08.15
no image
[백준-2407] 조합
dp 를 사용하여 빠른 시간에 조합 값을 구하는 문제였다. n과 m에 따라 값을 구하기만 하면 되는데 이 때 수의 범위가 long long 을 벗어나기 떄문에 string 으로 합을 처리해주는 로직이 필요하다. 이를 위해 밑의 코드에서는 get_sum 이라는 함수에서 string을 통해 수의 합을 표현해주어 문제를 풀 수 있었다. #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef long long LL; string dp[103][103]; string get_sum(st..
2021.08.13
no image
[백준-13460] 구슬 탈출 2
간단한 bfs 이지만 경우의 수가 매우 많은 귀찮은(?) 문제였다. 일단 시간제한은 2초 이지만 보드의 가로 세로가 최대 10 이므로 어떻게든 지지고 볶아도 시간 초과는 나지 않는 문제였다. 때문에 구현에만 정성을 쏟으면 된다. 로직은 다음과 같다. 위, 아래, 오른쪽, 왼쪽 4가지로 구분한다. 각 4가지별로 빨강이 앞에 있는 경우, 뒤에 있는 경우로 나눈다. 나눈 경우에 따라 while 문으로 벽이 나올 때까지 빨간 공과 파란 공을 움직인다. (이 때 서로 앞지르지는 못한다) 위의 1→2→3 을 반복하면 간단하게 문제가 풀린다. 이 때 시간 복잡도는 이전에 위로 움직인 경우 다음에 위로 움직이지 않게끔 하여 최대 3^10 정도가 나오게 된다. 대략 6만 정도가 나오게 되고, 각 케이스마다 1000줄씩 ..
2021.08.12
no image
[백준-1647] 도시 분할 계획
최소 스패닝 트리 문제이다. 일단 시간제한부터 보면 2초의 제한을 가지고 있다. 그 후 입력값의 범위를 보면 100000 개 이하의 노드와 1000000(백만개) 이하의 edge 가 있는 것을 볼 수 있다. 이 문제를 크루스칼을 통해 구하게 되면 nlog(n) 의 복잡도를 가지게 되어 널널하게 통과할 수 있다. (크루스칼 → 사실상 정렬알고리즘과 union-find 알고리즘의 복잡도와 비슷하다.) 여튼 크루스칼 알고리즘으로 최소 weight 를 가지는 edge 부터 차근차근 cycle 이 있는지 확인해가며 처리해주면 쉽게 풀리는 문제이다. #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #includ..
2021.08.12
no image
[백준-1021] 회전하는 큐
1021번: 회전하는 큐 단순한 구현문제이다. 시간제한은 2초임에 불구하고 입력값의 크기가 50이므로 n^2 의 복잡도를 가질 시 2500 밖에 걸리지 않아 매우 널널한 문제이다. index 를 가지고 0일때 왼쪽으로 돌리면 다시 오른쪽 끝으로, 반대로 n일 때 오른쪽으로 돌리면 0으로.. 의 로직을 기준으로 코드를 짜면 끝나는 코드이다. 밑의 코드는 단순 무지성으로 코드만 짠거긴 한데 중간중간 왼쪽으로 돌릴지, 오른쪽으로 돌릴지에 대해 그리디 방식으로 구하게 되면 코드가 더 깔끔하게 나오게 된다. #define _CRT_SECURE_NO_WARNINGS #include #include using namespace std; int num[100]; int check[100]; int arr[100]; i..
2021.08.12
no image
[백준-2346] 풍선 터뜨리기
2346번: 풍선 터뜨리기 풍선을 터뜨리고 각 index 별로 터지는 순서를 적어야하는 줄 알고 삽질한 문제이다. 일단 시간제한이 2초에 입력값의 범위가 1000까지라 n^2 의 복잡도로는 널널하게 패스하기 때문에 넉넉잡아 풀어도 상관 없을거라고 생각한다. 로직은 단순하게 index 를 돌려가면서 0이 되었을 때, n-1 이 되었을 때 나누어서 풀면 된다. 코드는 다음과 같다. #define _CRT_SECURE_NO_WARNINGS #include #include using namespace std; int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); int n, input; deque dq; cin >> n; fo..
2021.08.12
no image
[백준-1629] 곱셈
1629번: 곱셈 시간복잡도를 먼저 봐야하는 문제이다. 0.5초의 시간제한에 2147483647 이하의 입력값에 직접 모든 계산을 돌린다면 엄청난 차이로 시간이 터지게 된다. 때문에 log(n) 이하의 알고리즘을 생각해야 한다. log 를 보자마자 절반으로 나눠볼까? 라는 생각을 한 뒤 분할하여 값을 구하면 되지 않을까 생각이 든다. 즉 다음과 같은 로직으로 코드를 짤 수 있다. 제곱수를 절반으로 쪼개 분할정복 틀 만든 뒤 return (?!) 생각보다 간단하게 끝난다. 다만 계산 중간 곱한 뒤 나누는 과정에서 int 의 범위를 벗어날 수 있기 때문에 long long 으로 처리해주어야 한다. #include using namespace std; typedef long long LL LL _get(LL ..
2021.08.12
no image
[백준-1918] 후위 표기식
1918번: 후위 표기식 경우의 수 나눠서 처리하면 된다. 입력받은 string 을 돌면서 숫자인 경우 answer 에 push 연산자인 경우 +, - 인 경우 이미 들어있는게 *, / 인 경우: 우선 순위가 높은 *, / 를 stack 에서 뺀 후 answer 에 push → 그리고 기존 거 stack 에 push 이미 들어있는게 +, - 인 경우: 먼저 들어있는 +, - 를 stack 에서 뺀 후 answer 에 push → 그리고 기존 거 stack 에 push *, / 인 경우 이미 들어있는게 *, / 인 경우: 먼저 들어있는 *, / 를 stack 에서 뺀 후 answer 에 push → 그리고 기존 거 stack 에 push 이미 들어있는게 +, - 인 경우: 기존의 *, / 가 우선순위 높으..
2021.08.11
no image
[백준-20040] 사이클 게임
https://www.acmicpc.net/problem/20040 20040번: 사이클 게임 사이클 게임은 두 명의 플레이어가 차례대로 돌아가며 진행하는 게임으로, 선 플레이어가 홀수 번째 차례를, 후 플레이어가 짝수 번째 차례를 진행한다. 게임 시작 시 0 부터 n − 1 까지 고유한 www.acmicpc.net union_find 이거 하나만 알면 풀 수 있는 문제이지 않을까 한다. 시간복잡도에 대해 알아보면 1초의 시간 제한이 있고, 입력된 선분 정보의 개수가 100만개이므로 최소 nlog(n) 의 시간복잡도로 풀어야 통과가 되는 문제이다. 이에 따라 바로 로직을 알아보면 다음과 같다. 입력받은 n 만큼 root 배열을 초기화 시켜준다. 하나씩 입력 받을 때마다 root 배열을 통해 set_uni..
2021.08.11

( "팰린드롬 "이란 뒤집어도 똑같은 수를 의미한다. )

일단 시간 제한이 0.5초(5천만줄) 정도이고, 입력의 크기가 2000 개의 수열의 크기, 백만개의 질문의 개수이다. 이를 보아 질문마다 2000 번의 연산을 하게 된다면 문제가 바로 터진다는 걸 알 수 있다.

때문에 백만개의 질문마다 log(n) 의 연산을 하던가 아예 처음에 다 계산한 이후 O(1) 의 복잡도로 답을 내버리던가 해야 한다.

 

팰린드롬은 대표적인 dp 문제라고 할 수 있다. 예를 들어

1, 2, 1, 3, 1, 2, 1

위와 같이 7개의 수가 있을 때 (3, 7) 이 팰린드롬인지 확인해보자.

(3,7)1, 3, 1, 2, 1 로 양끝의 수가 같은 팰린드롬 같은(?) 수이다. 이 때 양끝의 수가 같으니 (3, 7) = (4, 6) 이라는 걸 유추할 수 있다. 이 상태 관계를 가지고 Dynamic Programming 을 해줄 수 있다.

즉, (a,b) 는 a번째 수와 b 번째 수가 같다면 (a,b) = (a+1, b-1) 이 되고, 당연히 같지 않다면 (a,b) = 0 이 된다.

#include <iostream>

using namespace std;

int arr[2001];
int dp[2001][2001];

void initialize() {
	for (int i = 0; i < n; i++) {
		for (int j =1; j <= n - i; j++) {
			if (i == 0) {
				dp[j][j + i] = 1;
			}
			else if (i == 1) {
				dp[j][j + i] = (arr[j] == arr[j + i]) ? 1 : 0;
			}
			else {
				if (arr[j] == arr[j + i]) 
					dp[j][j + i] = dp[j + 1][j + i - 1];
				else 
					dp[j][j + i] = 0;
			}
		}
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int n, m, a, b;
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> arr[i];

	// initialize dp arr
	initialize();

	cin >> m;
	for (int i = 0; i < m; i++) {
		cin >> a >> b;
		cout << dp[a][b] << "\n";
	}
	
	return 0;
}
 

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

 

10942번: 팰린드롬?

총 M개의 줄에 걸쳐 홍준이의 질문에 대한 명우의 답을 입력으로 주어진 순서에 따라서 출력한다. 팰린드롬인 경우에는 1, 아닌 경우에는 0을 출력한다.

www.acmicpc.net

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-2357] 최솟값과 최댓값  (0) 2021.08.16
[백준-2285] 우체국  (0) 2021.08.16
[백준-2407] 조합  (0) 2021.08.13
[백준-13460] 구슬 탈출 2  (0) 2021.08.12

dp 를 사용하여 빠른 시간에 조합 값을 구하는 문제였다. n과 m에 따라 값을 구하기만 하면 되는데 이 때 수의 범위가 long long 을 벗어나기 떄문에 string 으로 합을 처리해주는 로직이 필요하다.

이를 위해 밑의 코드에서는 get_sum 이라는 함수에서 string을 통해 수의 합을 표현해주어 문제를 풀 수 있었다.

 

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>


using namespace std;
typedef long long LL;
string dp[103][103];

string get_sum(string a, string b) {
	string res = "";
	char tp_str;
	int max_size = max(a.size(), b.size());
	int min_size = min(a.size(), b.size());
	int tp_int, tp_nxt = 0;

	deque<char> dq;
	for (int i = 0; i < min_size; i++) {
		tp_int = a.back() - '0' + b.back() - '0' + tp_nxt;
		if (tp_int >= 10) {
			tp_nxt = 1;
			tp_int -= 10;
		}
		else {
			tp_nxt = 0;
		}
		a.pop_back();
		b.pop_back();
		tp_str = tp_int + '0';
		dq.push_front(tp_str);
	}

	if (a.empty() && !b.empty()) {
		for (int i = min_size; i < max_size; i++) {
			tp_int = b.back() - '0' + tp_nxt;
			if (tp_int >= 10) {
				tp_nxt = 1;
				tp_int -= 10;
			}
			else {
				tp_nxt = 0;
			}
			b.pop_back();
			tp_str = tp_int + '0';
			dq.push_front(tp_str);
		}
	}
	else if(!a.empty() && b.empty()){
		for (int i = min_size; i < max_size; i++) {
			tp_int = a.back() - '0' + tp_nxt;
			if (tp_int >= 10) {
				tp_nxt = 1;
				tp_int -= 10;
			}
			else {
				tp_nxt = 0;
			}
			a.pop_back();
			tp_str = tp_int + '0';
			dq.push_front(tp_str);
		}
	}
	if (tp_nxt > 0) {
		tp_str = tp_nxt + '0';
		dq.push_front(tp_str);
	}
	while (!dq.empty()) {
		res.push_back(dq.front());
		dq.pop_front();
	}
	return res;
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	int n, m;
	cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		dp[i][1] = to_string(i);
		dp[i][0] = "1";
		dp[i][i] = "1";
	}

	for (int i = 3; i <= n; i++) {
		for (int j = 2; j < i; j++) {
			dp[i][j] = get_sum(dp[i - 1][j - 1], dp[i - 1][j]);
		}
	}

	cout << dp[n][m];
	return 0;
}

 

 

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

 

2407번: 조합

n과 m이 주어진다. (5 ≤ n ≤ 100, 5 ≤ m ≤ 100, m ≤ n)

www.acmicpc.net

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-2285] 우체국  (0) 2021.08.16
[백준-10943] 팰린드롬?  (0) 2021.08.15
[백준-13460] 구슬 탈출 2  (0) 2021.08.12
[백준-1647] 도시 분할 계획  (0) 2021.08.12

간단한 bfs 이지만 경우의 수가 매우 많은 귀찮은(?) 문제였다. 일단 시간제한은 2초 이지만 보드의 가로 세로가 최대 10 이므로 어떻게든 지지고 볶아도 시간 초과는 나지 않는 문제였다.

때문에 구현에만 정성을 쏟으면 된다. 로직은 다음과 같다.

  1. 위, 아래, 오른쪽, 왼쪽 4가지로 구분한다.
  2. 각 4가지별로 빨강이 앞에 있는 경우, 뒤에 있는 경우로 나눈다.
  3. 나눈 경우에 따라 while 문으로 벽이 나올 때까지 빨간 공과 파란 공을 움직인다. (이 때 서로 앞지르지는 못한다)

위의 1→2→3 을 반복하면 간단하게 문제가 풀린다. 이 때 시간 복잡도는 이전에 위로 움직인 경우 다음에 위로 움직이지 않게끔 하여 최대 3^10 정도가 나오게 된다. 대략 6만 정도가 나오게 되고, 각 케이스마다 1000줄씩 있다고 가정해도 6천만 정도의 넉넉한 복잡도가 나오게 된다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>

#define RED 2
#define BLUE 3
#define UP 100
#define DOWN 101
#define LEFT 102
#define RIGHT 103

using namespace std;
typedef long long LL;
typedef pair<int, int> pii;

int n, m;
int check[12][12];
int dx[] = { -1,0,1,0 };
int dy[] = { 0,-1,0,1 };
int dir[] = { UP, DOWN, LEFT, RIGHT };

int _min = 100;
void dfs(int _cnt, int _dir, pii _red, pii _blue) {
	if (_cnt > 10) return;
	
	if (_dir == UP) {
		if (_red.first < _blue.first) {
			// red 가 더 위에 있는 경우
			bool checkout = false;
			while (1) {
				if (check[_red.first - 1][_red.second] == -1) break;
				else if (check[_red.first - 1][_red.second] == 1) {
					checkout = true;
					break;
				}
				else {
					_red.first -= 1;
				}
			}
			while (1) {
				if (!checkout && _blue.first - 1 == _red.first && _blue.second == _red.second) break;
				else if (check[_blue.first - 1][_blue.second] == -1) break;
				else if (check[_blue.first - 1][_blue.second] == 1) {
					return;
				}
				else {
					_blue.first -= 1;
				}
			}
			if(checkout ) _min = min(_min, _cnt);
		}
		else {
			// blue 가 더 위에 있는 경우
			while (1) {
				if (check[_blue.first - 1][_blue.second] == -1) break;
				else if (check[_blue.first - 1][_blue.second] == 1) {
					return;
				}
				else {
					_blue.first -= 1;
				}
			}
			while (1) {
				if (_blue.first + 1 == _red.first && _blue.second == _red.second) break;
				if (check[_red.first - 1][_red.second] == -1) break;
				else if (check[_red.first - 1][_red.second] == 1) {
					_min = min(_min, _cnt);
					return;
				}
				else {
					_red.first -= 1;
				}
			}
		}
	}
	else if (_dir == DOWN) {
		if (_red.first < _blue.first) {
			// red 가 더 위에 잇는 경우
			
			while (1) {
				if (check[_blue.first + 1][_blue.second] == -1) break;
				else if (check[_blue.first + 1][_blue.second] == 1) {
					return;
				}
				else {
					_blue.first += 1;
				}
			}
			while (1) {
				if (_red.first + 1 == _blue.first && _blue.second == _red.second) break;
				else if (check[_red.first + 1][_red.second] == -1) break;
				else if (check[_red.first + 1][_red.second] == 1) {
					_min = min(_min, _cnt);
					return;
				}
				else {
					_red.first += 1;
				}
			}
		}
		else {
			// red가 밑에 있는 경우
			bool checkout = false;
			while (1) {
				if (check[_red.first + 1][_red.second] == -1) break;
				else if (check[_red.first + 1][_red.second] == 1) {
					checkout = true;
					break;
				}
				else _red.first += 1;
			}
			while (1) {
				if (!checkout && _blue.first + 1 == _red.first && _blue.second == _red.second) break;
				else if (check[_blue.first + 1][_blue.second] == -1) break;
				else if (check[_blue.first + 1][_blue.second] == 1) {
					return;
				}
				else 	_blue.first += 1;
			}
			if(checkout) _min = min(_min, _cnt);
		}
	}
	else if (_dir == LEFT) {
		if (_red.second > _blue.second) {
			// red 가 오른쪽에 있는 경우
			while (1) {
				if (check[_blue.first][_blue.second - 1] == -1) break;
				else if (check[_blue.first][_blue.second - 1] == 1) {
					return;
				}
				else 	_blue.second -= 1;
			}
			while (1) {
				if (_red.second - 1 == _blue.second && _blue.first == _red.first) break;
				else if (check[_red.first][_red.second - 1] == -1) break;
				else if (check[_red.first][_red.second - 1] == 1) {
					_min = min(_min, _cnt);
					return;
				}
				else 	_red.second -= 1;
			}
		}
		else {
			// red 가 왼쪽에 있는 경우
			bool checkout = false;
			while (1) {
				if (check[_red.first][_red.second - 1] == -1) break;
				else if (check[_red.first][_red.second - 1] == 1) {
					checkout = true;
					break;
				}
				else 	_red.second -= 1;
			}
			while (1) {
				if (!checkout && _blue.second - 1 == _red.second && _blue.first == _red.first) break;
				else if (check[_blue.first][_blue.second - 1] == -1) break;
				else if (check[_blue.first][_blue.second - 1] == 1) {
					return;
				}
				else 	_blue.second -= 1;
			}
			if(checkout) _min = min(_min, _cnt);
		}
	}
	else if (_dir == RIGHT) {
		if (_red.second > _blue.second) {
			// red 가 오른쪽에 있는 경우
			bool checkout = false;
			while (1) {
				if (check[_red.first][_red.second + 1] == -1) break;
				else if (check[_red.first][_red.second + 1] == 1) {
					checkout = true;
					break;
				}
				else 	_red.second += 1;
			}
			while (1) {
				if (!checkout && _blue.second + 1 == _red.second && _blue.first == _red.first) break;
				else if (check[_blue.first][_blue.second + 1] == -1) break;
				else if (check[_blue.first][_blue.second + 1] == 1) {
					return;
				}
				else 	_blue.second += 1;
			}
			if(checkout )_min = min(_min, _cnt);
		}
		else {
			// red 가 왼쪽에 있는 경우
			while (1) {
				if (check[_blue.first][_blue.second + 1] == -1) break;
				else if (check[_blue.first][_blue.second + 1] == 1) {
					return;
				}
				else 	_blue.second += 1;
			}
			while (1) {
				if (_red.second + 1 == _blue.second && _blue.first == _red.first) break;
				else if (check[_red.first][_red.second +1] == -1) break;
				else if (check[_red.first][_red.second + 1] == 1) {
					_min = min(_min, _cnt);
					return;
				}
				else 	_red.second += 1;
			}
		}
	}
	for (int i = 0; i < 4; i++) {
		if (dir[i] == _dir) continue;
		dfs(_cnt + 1, dir[i], _red, _blue);
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin >> n >> m;
	
	string row;
	pii red, blue;
	for (int i = 1; i <= n; i++) {
		cin >> row;
		for (int j = 1; j <= m; j++) {
			if (row.at(j-1) == '#') check[i][j] = -1;
			else if (row.at(j-1) == '.') check[i][j] = 0;
			else if (row.at(j-1) == 'O') check[i][j] = 1;
			else if (row.at(j-1) == 'R') 	red = { i,j };
			else if (row.at(j-1) == 'B')	blue = {i,j};
		}
	}

	// 기울이는 행동 자체를 탐색
	for (int i = 0; i < 4; i++) {
		dfs(1, dir[i], red, blue);
	}

	if (_min == 100) cout << -1;
	else cout << _min;

	return 0;
}

 

 

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

 

13460번: 구슬 탈출 2

첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'

www.acmicpc.net

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-10943] 팰린드롬?  (0) 2021.08.15
[백준-2407] 조합  (0) 2021.08.13
[백준-1647] 도시 분할 계획  (0) 2021.08.12
[백준-1021] 회전하는 큐  (0) 2021.08.12

최소 스패닝 트리 문제이다.

일단 시간제한부터 보면 2초의 제한을 가지고 있다. 그 후 입력값의 범위를 보면 100000 개 이하의 노드와 1000000(백만개) 이하의 edge 가 있는 것을 볼 수 있다.

이 문제를 크루스칼을 통해 구하게 되면 nlog(n) 의 복잡도를 가지게 되어 널널하게 통과할 수 있다. (크루스칼 → 사실상 정렬알고리즘과 union-find 알고리즘의 복잡도와 비슷하다.)

여튼 크루스칼 알고리즘으로 최소 weight 를 가지는 edge 부터 차근차근 cycle 이 있는지 확인해가며 처리해주면 쉽게 풀리는 문제이다.

 

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>


using namespace std;

int n, m;
int root[100001];
struct Node {
	int to, from, weight;
	Node(int a, int b, int c) : to(a), from(b), weight(c) {};
};

struct compare {
	bool operator()(Node a, Node b) {
		return a.weight > b.weight;
	}
};
int get_union(int a) {
	if (root[a] == a) return a;
	else {
		return get_union(root[a]);
	}
}

void set_union(int a, int b) {
	int ta = get_union(a);
	int tb = get_union(b);

	if (ta < tb) root[tb] = ta;
	else root[ta] = tb;
}
priority_queue<Node, vector<Node>, compare> pq;
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin >> n >> m;
	
	int ta, tb, tc;
	for (int i = 0; i < m; i++) {
		cin >> ta >> tb >> tc;
		Node tp(ta, tb, tc);
		pq.push(tp);
	}
	for (int i = 1; i <= n; i++) 
		root[i] = i;
	

	int _to, _from, _weight, res = 0;
	int _max = 0;
	while (!pq.empty()) {
		_to = pq.top().to;
		_from = pq.top().from;
		_weight = pq.top().weight;
		pq.pop();

		if (get_union(_to) == get_union(_from)) continue;
		else {
			set_union(_to, _from);
			res += _weight;
			_max = max(_max, _weight);
		}
	}
	cout << res - _max;
	return 0;
}
 

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

 

1647번: 도시 분할 계획

첫째 줄에 집의 개수N, 길의 개수M이 주어진다. N은 2이상 100,000이하인 정수이고, M은 1이상 1,000,000이하인 정수이다. 그 다음 줄부터 M줄에 걸쳐 길의 정보가 A B C 세 개의 정수로 주어지는데 A번 집

www.acmicpc.net

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-2407] 조합  (0) 2021.08.13
[백준-13460] 구슬 탈출 2  (0) 2021.08.12
[백준-1021] 회전하는 큐  (0) 2021.08.12
[백준-2346] 풍선 터뜨리기  (0) 2021.08.12
1021번: 회전하는 큐

 

단순한 구현문제이다.

시간제한은 2초임에 불구하고 입력값의 크기가 50이므로 n^2 의 복잡도를 가질 시 2500 밖에 걸리지 않아 매우 널널한 문제이다.

index 를 가지고 0일때 왼쪽으로 돌리면 다시 오른쪽 끝으로, 반대로 n일 때 오른쪽으로 돌리면 0으로.. 의 로직을 기준으로 코드를 짜면 끝나는 코드이다.

밑의 코드는 단순 무지성으로 코드만 짠거긴 한데 중간중간 왼쪽으로 돌릴지, 오른쪽으로 돌릴지에 대해 그리디 방식으로 구하게 되면 코드가 더 깔끔하게 나오게 된다.

 

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <algorithm>


using namespace std;

int num[100];
int check[100];
int arr[100];
int n, m;

int get_right_num(int a) {
	return (a >= n) ? 1 : a + 1;
}
int get_left_num(int a) {
	return (a == 1) ? n : a - 1;
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) num[i] = i;
	for (int i = 1; i <= m; i++) cin >> arr[i];
	
	int idx = 1;
	int res = 0, left_idx, right_idx, left_cnt, right_cnt;
	for (int i = 1; i <= m; i++) {
		left_idx = idx, left_cnt = 0;
		right_idx = idx, right_cnt = 0;

		while (num[left_idx] != arr[i]) {
			left_idx = get_left_num(left_idx);
			if (check[left_idx] == 0) left_cnt += 1;
		}

		while (num[right_idx] != arr[i]) {
			right_idx = get_right_num(right_idx);
			if (check[right_idx] == 0) right_cnt += 1;
		}
		
		idx = get_right_num(right_idx);
		while (check[idx] != 0) {
			idx = get_right_num(idx);
		}
		check[right_idx] = 1;
		res += min(left_cnt, right_cnt);
	}
	cout << res;
	return 0;
}

 

 

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-13460] 구슬 탈출 2  (0) 2021.08.12
[백준-1647] 도시 분할 계획  (0) 2021.08.12
[백준-2346] 풍선 터뜨리기  (0) 2021.08.12
[백준-1629] 곱셈  (0) 2021.08.12
2346번: 풍선 터뜨리기

 

풍선을 터뜨리고 각 index 별로 터지는 순서를 적어야하는 줄 알고 삽질한 문제이다.

일단 시간제한이 2초에 입력값의 범위가 1000까지라 n^2 의 복잡도로는 널널하게 패스하기 때문에 넉넉잡아 풀어도 상관 없을거라고 생각한다.

로직은 단순하게 index 를 돌려가면서 0이 되었을 때, n-1 이 되었을 때 나누어서 풀면 된다. 코드는 다음과 같다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <queue>

using namespace std;

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	int n, input;
	deque<int> dq;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> input;
		dq.push_back(input);
	}
	
	int check[1001] = { 0, };
	int cnt = 2, cx, cidx = 0;
	check[0] = 1;
	cout << 1 << " ";
	for(int i=2 ; i<=n ; i++){
		cx = dq[cidx];
		// cidx 풍선 터뜨림

		int ct = 0;
		if (cx < 0) {
			while (ct < -1 * cx) {
				cidx = (cidx == 0) ? n - 1 : cidx - 1;
				if (check[cidx] != 0) continue;
				else ct++;
			}
		}
		else {
			while (ct < cx) {
				cidx = (cidx == n - 1) ? 0 : cidx + 1;
				if (check[cidx] != 0) continue;
				else ct++;
			}
		}
		check[cidx] = i;
		cout << cidx+1 << " ";
	}

	return 0;
}

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-1647] 도시 분할 계획  (0) 2021.08.12
[백준-1021] 회전하는 큐  (0) 2021.08.12
[백준-1629] 곱셈  (0) 2021.08.12
[백준-1918] 후위 표기식  (1) 2021.08.11
1629번: 곱셈
 

 

시간복잡도를 먼저 봐야하는 문제이다.

0.5초의 시간제한에 2147483647 이하의 입력값에 직접 모든 계산을 돌린다면 엄청난 차이로 시간이 터지게 된다. 때문에 log(n) 이하의 알고리즘을 생각해야 한다.

log 를 보자마자 절반으로 나눠볼까? 라는 생각을 한 뒤 분할하여 값을 구하면 되지 않을까 생각이 든다. 즉 다음과 같은 로직으로 코드를 짤 수 있다.

  1. 제곱수를 절반으로 쪼개 분할정복
  1. 틀 만든 뒤 return (?!)

생각보다 간단하게 끝난다. 다만 계산 중간 곱한 뒤 나누는 과정에서 int 의 범위를 벗어날 수 있기 때문에 long long 으로 처리해주어야 한다.

 

#include <iostream>

using namespace std;
typedef long long LL

LL _get(LL x, LL y, LL z) {
	if (y == 1) return x % z;
	else {
		LL half = _get(x, y / 2, z);
		if (y % 2 == 0) {
			return (half * half) % z;
		}
		else {
			return (x * ((half * half) % z)) % z;
		}
	}
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	LL a, b, c;
	cin >> a >> b >> c;

	cout << _get(a, b, c);
	return 0;
}

 

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-1021] 회전하는 큐  (0) 2021.08.12
[백준-2346] 풍선 터뜨리기  (0) 2021.08.12
[백준-1918] 후위 표기식  (1) 2021.08.11
[백준-20040] 사이클 게임  (0) 2021.08.11
1918번: 후위 표기식

경우의 수 나눠서 처리하면 된다.

입력받은 string 을 돌면서

  1. 숫자인 경우 answer 에 push
  1. 연산자인 경우
    1. +, - 인 경우
      1. 이미 들어있는게 *, / 인 경우: 우선 순위가 높은 *, / 를 stack 에서 뺀 후 answer 에 push → 그리고 기존 거 stack 에 push
      2. 이미 들어있는게 +, - 인 경우: 먼저 들어있는 +, - 를 stack 에서 뺀 후 answer 에 push → 그리고 기존 거 stack 에 push
    1. *, / 인 경우
      1. 이미 들어있는게 *, / 인 경우: 먼저 들어있는 *, / 를 stack 에서 뺀 후 answer 에 push → 그리고 기존 거 stack 에 push
      2. 이미 들어있는게 +, - 인 경우: 기존의 *, / 가 우선순위 높으니 stack 에 바로 push
    1. (, ) 인 경우
      1. ( 인 경우 : answer 에 일단 push
      2. ) 인 경우 : ( 가 나올때까지 "pop → answer 에 push" 반복

 

이 로직을 직접 구현하면 처리 된다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef tuple<int, int, string> tii;
int root[500001];

bool check_oper(char c) {
	return (c == '*' || c == '+' || c == '-' || c == '/' || c == '(' || c == ')');
}
bool div_mul(char c) {
	return c == '*' || c == '/';
}
bool plu_min(char c) {
	return c == '+' || c == '-';
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	string n;
	cin >> n;

	stack<char> st;
	string answer;
	for (int i = 0; i < n.size(); i++) {
		if (check_oper(n.at(i))) {
			if (st.empty()) 
				st.push(n.at(i));
			else {
				if (div_mul(st.top())){
					if (plu_min(n.at(i))) {
						while (1) {
							answer.push_back(st.top());
							st.pop();
							if (st.empty()) break;
							if (plu_min(st.top())) continue;
							else break;
						}
						st.push(n.at(i));
					}
					else if (div_mul(n.at(i))) {
						answer.push_back(st.top());
						st.pop();
						st.push(n.at(i));
					}
					else {
						if (n.at(i) == '(') st.push(n.at(i));
						else if (n.at(i) == ')') {
							while (st.top() != '(') {
								answer.push_back(st.top());
								st.pop();
							}
							st.pop(); // ( 제거
						}
					}
				}
				else if (plu_min(st.top())) {
					if (n.at(i) == ')') {
						while (st.top() != '(') {
							answer.push_back(st.top());
							st.pop();
						}
						if (st.top() == '(') st.pop(); // ( 제거
					}
					else if (plu_min(n.at(i))) {
						answer.push_back(st.top());
						st.pop();
						st.push(n.at(i));
					}
					else {
						st.push(n.at(i));
					}
				}
				else if(st.top() == '(' || st.top() == ')'){
					if (n.at(i) == ')') {
						while (st.top() != '(') {
							answer.push_back(st.top());
							st.pop();
						}
						if(st.top() == '(') st.pop(); // ( 제거
					}
					else st.push(n.at(i));
				}
			}
		}
		// 숫자인 경우
		else {
			answer.push_back(n.at(i));
		}
	}
	while (!st.empty()) {
		answer.push_back(st.top());
		st.pop();
	}
	cout << answer;

	return 0;
}

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-2346] 풍선 터뜨리기  (0) 2021.08.12
[백준-1629] 곱셈  (0) 2021.08.12
[백준-20040] 사이클 게임  (0) 2021.08.11
[백준-11000] 강의실 배정  (0) 2021.08.11

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

 

20040번: 사이클 게임

사이클 게임은 두 명의 플레이어가 차례대로 돌아가며 진행하는 게임으로, 선 플레이어가 홀수 번째 차례를, 후 플레이어가 짝수 번째 차례를 진행한다. 게임 시작 시 0 부터 n − 1 까지 고유한

www.acmicpc.net

 

union_find 이거 하나만 알면 풀 수 있는 문제이지 않을까 한다.

시간복잡도에 대해 알아보면 1초의 시간 제한이 있고, 입력된 선분 정보의 개수가 100만개이므로 최소 nlog(n) 의 시간복잡도로 풀어야 통과가 되는 문제이다. 이에 따라 바로 로직을 알아보면 다음과 같다.

  1. 입력받은 n 만큼 root 배열을 초기화 시켜준다.
  2. 하나씩 입력 받을 때마다 root 배열을 통해 set_union 해준다. (하나로 합쳐준다.)
  3. 합쳐줄 때 이미 같은 root 를 가지고 있었을 경우 바로 해당 index 를 출력하고 프로그램을 끝낸다.

위 로직으로 풀면 트리 형태의 union_find 알고리즘으로 인해 선분 하나씩 입력할 때마다 평균 log(n)의 복잡도가 생기게 되어 총 nlog(n) 의 복잡도로 간단하게 풀린다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef tuple<int, int, string> tii;
int root[500001];
int find_union(int a) {
    if (root[a] == a)
        return a;
    else {
        return find_union(root[a]);
    }
}
bool set_union(int a, int b) {
    int x = find_union(a);
    int y = find_union(b);
    if (x == y) return false;
    if (x < y) root[y] = x;
    else root[x] = y;
    return true;
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    int n, m, a, b;
    cin >> n >> m;

    for (int i = 0; i < n; i++)
        root[i] = i;
    for (int i = 1; i <= m; i++) {
        cin >> a >> b;
        if (!set_union(a, b)) {
            cout << i;
            break;
        }
        if (i == m) {
            cout << 0; 
            break;
        }
    }

    return 0;
}

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-1629] 곱셈  (0) 2021.08.12
[백준-1918] 후위 표기식  (1) 2021.08.11
[백준-11000] 강의실 배정  (0) 2021.08.11
[프로그래머스] 큰수만들기  (0) 2021.08.11