no image
[백준-2075] N번째 큰 수
생각보다 간단한 문제였다. N번째 큰 수를 찾아야하므로 크기가 N 초과가 되면 안되는 우선순위 큐를 선언한 다음에 하나씩 입력받다가 N 초과가 되는 경우 제일 작은 값을 뺴주어 N개를 유지시키는 식으로 쭉 입력받고 맨 마지막에 우선순위 큐에 있는 N개의 수 중에서 제일 작은 값이 N번째 큰 수가 된다. #define _CRT_SECURE_NO_WARNINGS #include #include using namespace std; priority_queue pq; int main() { int n, input; scanf("%d", &n); for (int i = 0; i n) { pq..
2021.08.19
no image
[백준-9935] 문자열폭발
단순 탐색 문제인것 같다. 문자와 정수 pair 를 저장하는 vector 를 만든다. 그 후 문자열의 값을 하나씩 확인하면서 문자를 vector 에 넣되, 넣을 때 비교할 폭탄 문자열과 확인하여 같은 값이라면 몇번재 index 와 같은지를 정수로 같이 넣어준다. 이로써 폭탄이 만일 터지더라도 그 다음에 저장되어있는 정수를 확인해 계속해서 이어지는 문자가 들어올 시 폭탄이 터지도록 로직을 구현할 수 있다. 복잡도는 100만 * 36 + 36n? 정도? 라서 2초에 비해 넉넉한 복잡도를 가질 것이라 생각된다. #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include..
2021.08.19
no image
[백준-2166] 다각형의 면적
#define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include using namespace std; typedef pair pii; int n, cnt = 0, a, b; priority_queue pq; vector v; int main() { int n; long double a, b; scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%Lf %Lf", &a, &b); v.push_back({ a, b }); } long double x1, y1, x2, y2, x3, y3; x1 = v[0].first; y1 = v[0].second; long d..
2021.08.19
no image
[백준-2239] 스도쿠
간단한 백트래킹을 사용한 구현 문제이다. 로직은 생각보다 간단하다. dfs 순회를 하면서 넣으려는 숫자가 행에 있는지 열에 있는지 박스에 있는지 3가지만 확인하면 된다. 이 때 각각을 전부 순회를 때리게 되면 각각 9번의 연산이 생기게 된다. 그렇게 되면 81번의 공간에 9개의 숫자를 확인하되 각각 27번의 check 연산을 하게 되어 복잡도에 큰 문제가 생기지 않는다고 판단된다. 허나 좀더 빠르게 연산하기 위해 해쉬를 두어 숫자를 체크하게 되면 메모리를 조금 더 두어 check 연산을 O(1) 만큼의 복잡도로 처리할 수 있게 된다. #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #..
2021.08.18
no image
[백준-2357] 최솟값과 최댓값
전형적인 세그먼트 트리 문제이다. 일단 시간 제한이 2초이기 때문에 일일이 10만개의 정수 쌍 입력에 대해 10만개의 배열에서 최솟값과 최댓값을 뒤지게 되면 복잡도가 터지게 된다. 이를 위해 미리 답들을 트리 형태로 저장해놓는 세그먼트 트리를 사용하여 복잡도를 줄일 수 있다. 값을 저장할 때마다 최솟값과 최댓값을 log(n) 의 복잡도로 저장해놓는 식이다. 그 후 정수 쌍 입력을 받을 때마다 만들어 두었던 세그먼트 트리에 query를 날리는 식으로 문제를 풀게 되면 답이 금방 나오게 된다. #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #include..
2021.08.16
no image
[백준-2285] 우체국
그리디 알고리즘 을 사용하는 문제였다. 일단 시간제한이 2초인데, 입력값이 (위치, 인구수) 쌍으로 10만개이다. 이를 일반적인 브루트포스로 풀게되면 전체 평균을 구하는 과정 중에 값의 범위 때문에 터질 수도 있다. 다만 시간으로는 안 터지지 않을까 싶다. 즉, 이 문제의 경우 그리디하게 위치를 정해주어야 하는데 그 로직은 다음과 같다. 입력받은 쌍을 위치 순으로 정렬한다. 해당 위치까지의 누적합을 배열에 저장해둔다. 그 후 이분탐색으로 왼쪽 누적합, 오른쪽 누적합의 차이가 최소인 지점을 찾는다. 생각보다 간단한 로직이다. 허나 평균을 구하는게 아닌 누적합 자체로 이분탐색을 한다는 것 자체가 생각하기 어렵기 때문에 경험이 필요한 문제라 생각한다. #define _CRT_SECURE_NO_WARNINGS ..
2021.08.16
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

생각보다 간단한 문제였다.

N번째 큰 수를 찾아야하므로 크기가 N 초과가 되면 안되는 우선순위 큐를 선언한 다음에 하나씩 입력받다가 N 초과가 되는 경우 제일 작은 값을 뺴주어 N개를 유지시키는 식으로 쭉 입력받고 맨 마지막에 우선순위 큐에 있는 N개의 수 중에서 제일 작은 값이 N번째 큰 수가 된다.

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <queue>

using namespace std;
priority_queue<int, vector<int>, greater<int>> pq;
int main()
{
    int n, input;
    scanf("%d", &n);
    for (int i = 0; i < n * n; i++) {
        scanf("%d", &input);
        pq.push(input);
        if (pq.size() > n) {
            pq.pop();
        }
    }

    printf("%d", pq.top());
    
    return 0;
}

 

 

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

 

2075번: N번째 큰 수

첫째 줄에 N(1 ≤ N ≤ 1,500)이 주어진다. 다음 N개의 줄에는 각 줄마다 N개의 수가 주어진다. 표에 적힌 수는 -10억보다 크거나 같고, 10억보다 작거나 같은 정수이다.

www.acmicpc.net

 

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

[백준-16724] 피리 부는 사나이  (0) 2021.08.20
[백준-12852] 1로 만들기 2  (0) 2021.08.20
[백준-9935] 문자열폭발  (0) 2021.08.19
[백준-2166] 다각형의 면적  (0) 2021.08.19

단순 탐색 문제인것 같다.

문자와 정수 pair 를 저장하는 vector 를 만든다. 그 후 문자열의 값을 하나씩 확인하면서 문자를 vector 에 넣되, 넣을 때 비교할 폭탄 문자열과 확인하여 같은 값이라면 몇번재 index 와 같은지를 정수로 같이 넣어준다.

이로써 폭탄이 만일 터지더라도 그 다음에 저장되어있는 정수를 확인해 계속해서 이어지는 문자가 들어올 시 폭탄이 터지도록 로직을 구현할 수 있다.

복잡도는 100만 * 36 + 36n? 정도? 라서 2초에 비해 넉넉한 복잡도를 가질 것이라 생각된다.

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <string>
#include <iostream>
#include <tuple>
#include <cmath>
#include <stack>

using namespace std;
typedef pair<char, int> pii;

int main()
{
    string a, b;
    vector<pii> st;

    cin >> a >> b;
    int cnt = 0;
    for (int i = 0; i < a.size(); i++) {
        if (st.empty() || st.back().second < 0) {
            st.push_back({ a.at(i), (b.at(0) == a.at(i) ? 0 : -1) });
        }
        else {
            if (st.back().second >= 0) {
                if (b.at(st.back().second + 1) == a.at(i)) 
                    st.push_back({ a.at(i), st.back().second + 1 });
                else 
                    st.push_back({ a.at(i), (b.at(0) == a.at(i) ? 0 : -1) });
            }
        }

        if (st.back().second == b.size() - 1) {
            for (int k = 0; k < b.size(); k++)  st.pop_back();
        }
    }
    if (st.empty()) cout << "FRULA\n";
    else {
        for (auto c : st) printf("%c", c);
    }
    

    return 0;
}

 

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

 

9935번: 문자열 폭발

첫째 줄에 문자열이 주어진다. 문자열의 길이는 1보다 크거나 같고, 1,000,000보다 작거나 같다. 둘째 줄에 폭발 문자열이 주어진다. 길이는 1보다 크거나 같고, 36보다 작거나 같다. 두 문자열은 모

www.acmicpc.net

 

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

[백준-12852] 1로 만들기 2  (0) 2021.08.20
[백준-2075] N번째 큰 수  (0) 2021.08.19
[백준-2166] 다각형의 면적  (0) 2021.08.19
[백준-2239] 스도쿠  (0) 2021.08.18
#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <string>
#include <tuple>
#include <cmath>

using namespace std;
typedef pair<long double, long double> pii;
int n, cnt = 0, a, b;
priority_queue<int, vector<int>, greater<int>> pq;
vector<pii> v;
int main()
{
    int n;
    long double a, b;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        scanf("%Lf %Lf", &a, &b);
        v.push_back({ a, b });
    }
    long double x1, y1, x2, y2, x3, y3;
    x1 = v[0].first;
    y1 = v[0].second;
    long double res = 0;
    for (int i = 1; i < v.size(); i++) {
        x2 = v[i - 1].first;
        y2 = v[i - 1].second;
        x3 = v[i].first;
        y3 = v[i].second;
        res += (x1 * y2 + x2 * y3 + x3 * y1) - (x2 * y1 + x3 * y2 + x1 * y3);
    }
    printf("%.1Lf", abs(res/2));
    return 0;
}

수학적 풀이가 필요한 문제이다...

일단 일반적으로 3개의 점이 주어진 삼각형을 구하는 공식은 오른쪽과 같다.

이 때 너비를 구할 때 절대값을 해주는 이유는 외적에서 방향성을 통해 양수 값의 너비를 구하기 위함이다. 보통 이런식으로 문제를 풀면 된다고 생각한다.

하지만 이 문제의 경우 주어진 점을 순서대로 이은 후에 마지막에 나타나는 다각형의 너비를 구하는 문제이므로 점을 이쁘게 모두 이은 다각형 모양이 아닌 순서가 약간 뒤바뀌어 이었을 때 나올 수 있는 여러개의 다각형의 너비를 구하는 식으로 풀어야 한다.

따라서 위의 공식에서 절대값을 뺀 값들을 계속 더하게 되면 어느 곳에서는 음수인 너비가 다른 곳에서 양수의 값으로 상쇄되는 식으로 계산되어 결국 마지막에 제대로 된 너비를 구할 수 있게 된다.... 이해가 잘 안된다면 너비 외적 구하기 검색 ㄱㄱ..

 

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

 

2166번: 다각형의 면적

첫째 줄에 N이 주어진다. 다음 N개의 줄에는 다각형을 이루는 순서대로 N개의 점의 x, y좌표가 주어진다. 좌표값은 절댓값이 100,000을 넘지 않는 정수이다.

www.acmicpc.net

 

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

[백준-2075] N번째 큰 수  (0) 2021.08.19
[백준-9935] 문자열폭발  (0) 2021.08.19
[백준-2239] 스도쿠  (0) 2021.08.18
[백준-2357] 최솟값과 최댓값  (0) 2021.08.16

간단한 백트래킹을 사용한 구현 문제이다.

로직은 생각보다 간단하다. dfs 순회를 하면서 넣으려는 숫자가

  • 행에 있는지
  • 열에 있는지
  • 박스에 있는지

3가지만 확인하면 된다. 이 때 각각을 전부 순회를 때리게 되면 각각 9번의 연산이 생기게 된다. 그렇게 되면 81번의 공간에 9개의 숫자를 확인하되 각각 27번의 check 연산을 하게 되어 복잡도에 큰 문제가 생기지 않는다고 판단된다.

허나 좀더 빠르게 연산하기 위해 해쉬를 두어 숫자를 체크하게 되면 메모리를 조금 더 두어 check 연산을 O(1) 만큼의 복잡도로 처리할 수 있게 된다.

 

#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;
int doqu[10][10];
int row[10][10];
int col[10][10];
int box[3][3][10];
vector<pii> zero;
bool dfs_check = false;

void do_memo(int tx, int ty, int x, int y, int i) {
	row[x][i] = 1;
	col[i][y] = 1;
	box[tx][ty][i] = 1;
	return;
}
void un_memo(int tx, int ty, int x, int y, int i) {
	row[x][i] = 0;
	col[i][y] = 0;
	box[tx][ty][i] = 0;
	return;
}
void print_doqu() {
	for (int i = 1; i <= 9; i++) {
		for (int j = 1; j <= 9; j++) {
			printf("%d", doqu[i][j]);
		}
		printf("\n");
	}
	return;
}
void dfs(int n) {
	if (dfs_check) return;
	if (n == zero.size()) {
		dfs_check = true;
		print_doqu();
		return;
	}
	int cx = zero[n].first;
	int cy = zero[n].second;
	int tp = 0, tx, ty;
	for (int i = 1; i <= 9; i++) {
		if (row[cx][i] == 1) continue;
		if (col[i][cy] == 1) continue;
		tx = (cx - 1) / 3;
		ty = (cy - 1) / 3;
		if (box[tx][ty][i] == 1) continue;

		tp = doqu[cx][cy];
		doqu[cx][cy] = i;
		do_memo(tx, ty, cx, cy, i);
		dfs(n + 1);
		un_memo(tx, ty, cx, cy, i);
		doqu[cx][cy] = tp;
		
	}
	
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	int tx, ty;
	for (int i = 1; i <= 9; i++) {
		for (int j = 1; j <= 9; j++) {
			scanf(" %1d", &doqu[i][j]);
			if (doqu[i][j] == 0)
				zero.push_back({ i,j });
			else {
				row[i][doqu[i][j]] = 1;
				col[doqu[i][j]][j] = 1;
				tx = (i - 1) / 3;
				ty = (j - 1) / 3;
				box[tx][ty][doqu[i][j]] = 1;
			}
		}
	}
	dfs(0);

	return 0;
}

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

 

2239번: 스도쿠

스도쿠는 매우 간단한 숫자 퍼즐이다. 9×9 크기의 보드가 있을 때, 각 행과 각 열, 그리고 9개의 3×3 크기의 보드에 1부터 9까지의 숫자가 중복 없이 나타나도록 보드를 채우면 된다. 예를 들어 다

www.acmicpc.net

 

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

[백준-9935] 문자열폭발  (0) 2021.08.19
[백준-2166] 다각형의 면적  (0) 2021.08.19
[백준-2357] 최솟값과 최댓값  (0) 2021.08.16
[백준-2285] 우체국  (0) 2021.08.16

전형적인 세그먼트 트리 문제이다. 일단 시간 제한이 2초이기 때문에 일일이 10만개의 정수 쌍 입력에 대해 10만개의 배열에서 최솟값과 최댓값을 뒤지게 되면 복잡도가 터지게 된다.

이를 위해 미리 답들을 트리 형태로 저장해놓는 세그먼트 트리를 사용하여 복잡도를 줄일 수 있다.

값을 저장할 때마다 최솟값과 최댓값을 log(n) 의 복잡도로 저장해놓는 식이다. 그 후 정수 쌍 입력을 받을 때마다 만들어 두었던 세그먼트 트리에 query를 날리는 식으로 문제를 풀게 되면 답이 금방 나오게 된다.

#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;
LL max_tree[1 << 20];
LL min_tree[1 << 20];
LL min_query_seg(int node, int s, int e, int l, int r) {
	if (r < s || e < l) return 1000000000;
	if (l <= s && e <= r) return min_tree[node];
	else
		return min(min_query_seg(node * 2, s, (s + e) / 2, l, r),
			min_query_seg(node * 2 + 1, (s + e) / 2 + 1, e, l, r));
	
}
LL max_query_seg(int node, int s, int e, int l, int r) {
	if (r < s || e < l) return 0;
	if (l <= s && e <= r) return max_tree[node];
	else 
		return max(max_query_seg(node * 2, s, (s + e) / 2, l, r),
			max_query_seg(node * 2 + 1, (s + e) / 2 + 1, e, l, r));
}
void update_seg(int node, int s, int e, int idx, int v) {
	if (idx < s || e < idx) return;
	if (s == e) {
		max_tree[node] = v;
		min_tree[node] = v;
	}
	else {
		update_seg(node * 2, s, (s + e) / 2, idx, v);
		update_seg(node * 2 + 1, (s + e) / 2 + 1, e, idx, v);
		max_tree[node] = max(max_tree[node * 2], max_tree[node * 2 + 1]);
		min_tree[node] = min(min_tree[node * 2], min_tree[node * 2 + 1]);
	}
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	LL n, m;
	LL a, b;
	scanf("%lld %lld", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a);
		update_seg(1, 1, n, i, a);
	}
	pair<LL, LL> ans;
	
	for (int i = 0; i < m; i++) {
		scanf("%lld %lld", &a, &b);
		printf("%lld ", min_query_seg(1, 1, n, a, b));
		printf("%lld\n", max_query_seg(1, 1, n, a, b));
	}
	
	return 0;
}
 

2357번: 최솟값과 최댓값

N(1 ≤ N ≤ 100,000)개의 정수들이 있을 때, a번째 정수부터 b번째 정수까지 중에서 제일 작은 정수, 또는 제일 큰 정수를 찾는 것은 어려운 일이 아니다. 하지만 이와 같은 a, b의 쌍이 M(1 ≤ M ≤ 100

www.acmicpc.net

 

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

[백준-2166] 다각형의 면적  (0) 2021.08.19
[백준-2239] 스도쿠  (0) 2021.08.18
[백준-2285] 우체국  (0) 2021.08.16
[백준-10943] 팰린드롬?  (0) 2021.08.15
그리디 알고리즘

을 사용하는 문제였다.

일단 시간제한이 2초인데, 입력값이 (위치, 인구수) 쌍으로 10만개이다. 이를 일반적인 브루트포스로 풀게되면 전체 평균을 구하는 과정 중에 값의 범위 때문에 터질 수도 있다. 다만 시간으로는 안 터지지 않을까 싶다.

즉, 이 문제의 경우 그리디하게 위치를 정해주어야 하는데 그 로직은 다음과 같다.

  1. 입력받은 쌍을 위치 순으로 정렬한다.
  2. 해당 위치까지의 누적합을 배열에 저장해둔다.
  3. 그 후 이분탐색으로 왼쪽 누적합, 오른쪽 누적합의 차이가 최소인 지점을 찾는다.

생각보다 간단한 로직이다. 허나 평균을 구하는게 아닌 누적합 자체로 이분탐색을 한다는 것 자체가 생각하기 어렵기 때문에 경험이 필요한 문제라 생각한다.

#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;

LL x[100001];
LL a[100001];

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int n;
	vector<pair<LL, LL>> v;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> x[i] >> a[i];
		v.push_back({ x[i], a[i] });
	}
	sort(v.begin(), v.end());

	vector<LL> sum;
	sum.push_back(v[0].second);
	for (int i = 1; i < n; i++) {
		sum.push_back(sum[i - 1] + v[i].second);
	}

	LL left = 0, right = n, mid;
	LL pos = 100000;
	while (left <= right) {
		mid = (left + right) / 2;
		if (sum[mid] < sum[n - 1] - sum[mid]) {
			left = mid + 1;
		}
		else if (sum[mid] > sum[n - 1] - sum[mid]) {
			right = mid - 1;
			pos = min(pos, v[mid].first);
		}
	}
	cout << pos;
	return 0;
}
 

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

 

2285번: 우체국

첫째 줄에 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 N개의 줄에는 X[1] A[1], X[2] A[2], …, X[N] A[N]이 주어진다. 범위는 |X[i]| ≤ 1,000,000,000, 0 ≤ A[i] ≤ 1,000,000,000 이며 모든 입력은 정수이다. 모든 A[i]를 합

www.acmicpc.net

 

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

[백준-2239] 스도쿠  (0) 2021.08.18
[백준-2357] 최솟값과 최댓값  (0) 2021.08.16
[백준-10943] 팰린드롬?  (0) 2021.08.15
[백준-2407] 조합  (0) 2021.08.13

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

일단 시간 제한이 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