※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 14266번 문제인 이모티콘이다.
문제는 아래 링크를 확인하자.

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

 

이모티콘이 \(N\)개 있는 상태에서 복사 연산을 한번 활용한 뒤 이후 새로 덮어쓰기 전까지의 붙여넣기 연산은 복사 직후에 몰아 하도록 소요 시간의 변화 없이 순서를 항상 조정할 수 있음을 관찰하자.

 

따라서 각 이모티콘의 상태에서 다음 상태로 가능한 상태들을 다음과 같이 좁혀 생각할 수 있다:

(1) 복사 후 연속하여 붙여넣기 연산만을 실행

(2) 이모티콘 하나를 삭제

 

위와 같은 간선의 개수는 생각보다 많지 않다. (조화급수를 생각해보자.) 따라서 이와 같은 그래프에서 dijkstra 알고리즘을 이용해 최소비용을 계산하는 것으로 문제를 충분히 빠르게 해결할 수 있다.

 

아래는 제출한 소스코드이다.

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

int dist[1001];
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
void dijkstra() {
	memset(dist, 0x3f, sizeof(dist));
	pq.push(make_pair(0, 1));
	dist[1] = 0;
	while (!pq.empty()) {
		int cur = pq.top().second, d = pq.top().first; pq.pop();
		if (dist[cur] < d) continue;
		for (int k = 1; k < cur; k++) {
			int nxt = cur - k, dd = d + k;
			if (dd < dist[nxt]) dist[nxt] = dd, pq.push(make_pair(dd, nxt));
		}
		for (int nxt = cur, dd = d + 1; nxt < 1001; nxt += cur, dd++) {
			if (dd < dist[nxt]) dist[nxt] = dd, pq.push(make_pair(dd, nxt));
		}
	}
}

int N;

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

	dijkstra();

	cin >> N; cout << dist[N];
}
728x90

※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 27296번 문제인 카탈란 마스터의 선분 그리기 게임이다.
문제는 아래 링크를 확인하자.

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

 

점이 3개 이상인 경우를 생각해보자. 그 외의 경우 답은 예제 출력과 같다.

 

원주를 따라 놓여있는 점들을 차례대로 이으면 볼록\(N\)각형을 얻을 수 있다. 이 볼록\(N\)각형의 각 변은 언제 그어도 다른 모든 선분과 교차하지 않으므로 이러한 \(N\)개의 선분은 항상 그을 수 있다.

 

그 외의 선분은 하나를 그을 때마다 위의 볼록\(N\)각형을 작은 다각형으로 분할하는 모양새를 띄게 되는데, 이것이 가능할 때까지 계속 선분을 그으면 결국 볼록\(N\)각형의 삼각분할(Triangulation)을 얻게 된다. 이 과정에서 긋게 되는 선분은 항상 \(N-3\)개이다.

 

따라서 \(N\)이 주어지면 항상 \(2N-3\)개의 선분을 긋게 됨을 알 수 있다. 이를 통해 게임의 승패를 구하고 문제를 해결하자.

 

지문의 카탈란 수 언급이 함정이라는 기여 의견이 보여 주관적인 생각을 같이 적어둔다. 지문에서 삼각분할이라는 표현을 직접 제시하지 않았을 뿐 카탈란 수를 직접 언급하고 있으므로 카탈란 수와 삼각분할의 관계를 알고 있는 사람이라면 매우 쉽게 풀 수 있는 문제라고 생각한다. 배경 지식을 쌓는 것을 게을리 하지 말자.

 

아래는 제출한 소스코드이다.

#include <iostream>
using namespace std;
typedef long long ll;

int T;
ll N;

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

	cin >> T;
	while (T--) {
		cin >> N;
		if (N < 2) cout << "1 0\n";
		else cout << "0 1\n";
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 6798 // C++] Knight Hop  (1) 2024.07.01
[BOJ 14266 // C++] 이모티콘  (0) 2024.06.30
[BOJ 5479 // C++] Pyramid  (0) 2024.06.28
[BOJ 21375 // C++] Konamikoden  (0) 2024.06.27
[BOJ 26092 // C++] 수학적인 최소 공통 조상  (1) 2024.06.26

※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 5479번 문제인 Pyramid이다.
문제는 아래 링크를 확인하자.

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

 

각 \(a\)열 \(b\)행의 사각형 영역 내부에 있는 \(c\)열 \(d\)행의 영역 중 "영역의 테두리 칸을 포함하지 않고, 안에 적힌 수의 합이 가장 작은 영역"을 빠르게 찾을 수 있다면 문제를 어렵지 않게 해결할 수 있을 것이다.

 

각 칸을 한쪽 구석으로 하는 \(c\)열 \(d\)행의 사각형 영역에 적힌 수의 합을 나타내는 2차원 배열을 하나 만들자. 이 배열에서의 사각형 영역에서의 최솟값 쿼리를 처리할 수 있다면 문제 해결에 어려운 점이 없을 것이다.

 

업데이트가 없는 2차원 배열에서의 사각형 영역 최솟값 쿼리는 전처리 후 2D Sparse Table, 2D Segment Tree 등을 이용하여 빠르게 구할 수 있다. 글쓴이는 (BOJ에 존재하는 태그 기준으로) Deque Range Minimum Trick을 활용해 문제를 해결하였다.

 

아래는 제출한 소스코드이다.

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

int R, C, RR, CC, RRR, CCC;
int A[1001][1001];
int B[1001][1001];
int psum[1001][1001];


int ans, ansrr, anscc, ansrrr, ansccc;
int ansmn = 0x3f3f3f3f;

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

    cin >> C >> R >> CC >> RR >> CCC >> RRR;
    for (int r = 1; r <= R; r++) {
        for (int c = 1; c <= C; c++) {
            cin >> A[r][c];
            psum[r][c] = A[r][c] + psum[r - 1][c] + psum[r][c - 1] - psum[r - 1][c - 1];
        }
    }
    for (int r = RRR; r <= R; r++) {
        for (int c = CCC; c <= C; c++) {
            B[r][c] = psum[r][c] - psum[r - RRR][c] - psum[r][c - CCC] + psum[r - RRR][c - CCC];
        }
    }
    for (int r = RR; r <= R; r++) {
        deque<pair<int, int>> dq;
        for (int cc = CCC + 1; cc < CC; cc++) { // 지금 영역: r - RR + 1 ~ r행, c - CC + 1 ~ c열
            int tmp = 0x3f3f3f3f;
            for (int rr = r - RR + 1 + RRR; rr < r; rr++) {
                tmp = min(tmp, B[rr][cc]);
            }
            while (!dq.empty() && dq.back().first >= tmp) dq.pop_back();
            dq.push_back(make_pair(tmp, cc));
        }
        for (int c = CC; c <= C; c++) {
            while (!dq.empty() && dq.front().second < c - CC + 1 + CCC) dq.pop_front();
            int X = psum[r][c] - psum[r - RR][c] - psum[r][c - CC] + psum[r - RR][c - CC];
            int Y = dq.front().first;
            int val = X - Y;
            if (ans < val) ans = val, ansrr = r, anscc = c;

            
            int tmp = 0x3f3f3f3f;
            for (int rr = r - RR + 1 + RRR; rr < r; rr++) {
                tmp = min(tmp, B[rr][c]);
            }
            while (!dq.empty() && dq.back().first >= tmp) dq.pop_back();
            dq.push_back(make_pair(tmp, c));
        }
    }

    for (int rr = ansrr - RR + 1 + RRR; rr < ansrr; rr++) {
        for (int cc = anscc - CC + 1 + CCC; cc < anscc; cc++) {
            if (ansmn > B[rr][cc]) ansmn = B[rr][cc], ansrrr = rr, ansccc = cc;
        }
    }

    cout << anscc - CC + 1 << ' ' << ansrr - RR + 1 << '\n';
    cout << ansccc - CCC + 1 << ' ' << ansrrr - RRR + 1 << '\n';
}

 

BOJ Random Defense #23

728x90

※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 21375번 문제인 Konamikoden이다.
문제는 아래 링크를 확인하자.

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

 

주어진 문자열에서 "UUNNVHVHBA"를 subsequence로 갖는 가장 짧은 substring의 길이를 찾는 문제이다. (그러한 substring이 있음이 보장된다.)

 

각 문자마다 다음으로 'U', 'N', 'V', 'H', 'B', 'A'가 등장하는 위치를 저장한 배열을 미리 만들어두면 각 문자마다 어느 인덱스까지 포함해야 "UUNNVHVHBA"를 부분문자열로 포함하게 되는지를 \(O(1)\)의 시간(9~10번의 인덱스 이동)에 계산할 수 있다. 각 문자별 배열을 계산할 때, 같은 인덱스에 중복해서 저장해야 하는 일이 없도록 구현하면 배열 하나당 \(O(N)\)의 시간복잡도로 구할 수 있다.

 

각 문자로부터 해당 문자열을 만들기 위해 포함해야하는 길이를 각각 구해 문제를 해결하자.

 

아래는 제출한 소스코드이다.

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

int K;
string A;
int U[200001], N[200001], V[200001], H[200001], B[200001], AA[200001];
int ans = 1000000007;

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

	cin >> A;
	K = A.length();
	for (int i = 0; i < K; i++) {
		if (A[i] == 'U') {
			for (int k = i - 1; k > -1 && !U[k]; k--) U[k] = i;
		}
		else if (A[i] == 'N') {
			for (int k = i - 1; k > -1 && !N[k]; k--) N[k] = i;
		}
		else if (A[i] == 'V') {
			for (int k = i - 1; k > -1 && !V[k]; k--) V[k] = i;
		}
		else if (A[i] == 'H') {
			for (int k = i - 1; k > -1 && !H[k]; k--) H[k] = i;
		}
		else if (A[i] == 'B') {
			for (int k = i - 1; k > -1 && !B[k]; k--) B[k] = i;
		}
		else if (A[i] == 'A') {
			for (int k = i - 1; k > -1 && !AA[k]; k--) AA[k] = i;
		}
	}

	for (int i = 0; i <= K; i++) {
		if (!U[i]) U[i] = K;
	}
	for (int i = 0; i <= K; i++) {
		if (!N[i]) N[i] = K;
	}
	for (int i = 0; i <= K; i++) {
		if (!V[i]) V[i] = K;
	}
	for (int i = 0; i <= K; i++) {
		if (!H[i]) H[i] = K;
	}
	for (int i = 0; i <= K; i++) {
		if (!B[i]) B[i] = K;
	}
	for (int i = 0; i <= K; i++) {
		if (!AA[i]) AA[i] = K;
	}

	for (int i = 0; i < K; i++) {
		if (A[i] != 'U') continue;
		int ii = AA[B[H[V[H[V[N[N[U[i]]]]]]]]];
		if (ii < K) ans = min(ans, ii - i + 1);
	}

	cout << ans - 10;
}
728x90

※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 26092번 문제인 수학적인 최소 공통 조상이다.
문제는 아래 링크를 확인하자.

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

 

두 정점 번호 \(x\)와 \(y\)가 가리키는 두 정점의 LCA(최소 공통 조상)는 \(x\)와 \(y\)의 공약수 중 하나가 될 것임은 쉽게 관찰할 수 있다. 이로부터, \(x\)와 \(y\)의 소인수 중 공약수에 포함될 수 없는 소인수가 모두 없어지기 전까지는 부모 정점으로 거슬러 올라가는 것을 반복해야 할 것임을 발견할 수 있다. 또한 각 정점에서 부모 정점으로 이동할 때 번호는 가장 작은 소인수부터 사라지므로, \(x\)와 \(y\)에 대하여 소인수 중 공약수에 포함되지 않는 최대의 소인수 '미만'의 모든 소인수는 지워야 함을 알 수 있다. 또한 공약수에 포함되지 않는 최대 소인수는 \(x\)와 \(y\)의 공약수에 포함된 개수만큼 남기고 나머지는 지워야 함을 알 수 있다.

 

위 과정에서 정점을 거슬러올라가는 총 횟수는 \(\lg{A}+\lg{B}\)  이하이므로 위와 같은 풀이를 통해 문제를 해결하자.

 

아래는 제출한 소스코드이다.

#include <iostream>
#include <utility>
using namespace std;
typedef long long ll;

ll A, B;

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

	cin >> A >> B;
	if (A < B) swap(A, B);
	for (ll i = 2; i < 1000001; i++) {
		if (A == B) {
			cout << A;
			return 0;
		}
		if (A % i == 0) A /= i;
		else if (B % i == 0) B /= i;
		else i++;
		i--;
		if (A < B) swap(A, B);
	}

	cout << 1;
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 5479 // C++] Pyramid  (0) 2024.06.28
[BOJ 21375 // C++] Konamikoden  (0) 2024.06.27
[BOJ 12946 // C++] 육각 보드  (0) 2024.06.25
[BOJ 7587 // C++] Anagrams  (0) 2024.06.24
[BOJ 27738 // C++] 연산자 파티  (0) 2024.06.23

※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 12946번 문제인 육각 보드이다.
문제는 아래 링크를 확인하자.

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

 

예제 입력 1과 같이 칠할 칸이 없다면 답은 0이 된다. 다음 문단부터는 칠할 칸이 존재한다고 가정하겠다.

 

세 가지 색의 이름을 각각 A, B, C라 할 때, 행마다 색을 "ABCABCABCA...", "CABCABCABC...", "BCABCABCAB..."와 같이 돌아가면서 칠하면 모든 칸을 인접한 칸의 색이 다르게 칠할 수 있음을 관찰하자. 따라서 문제의 답은 항상 3 이하이다.

 

한편, 어떤 칸을 두 가지 이하의 색으로 칠할 수 있다는 것은 칸을 노드로 갖고 인접관계를 에지로 갖는 그래프가 이분 그래프(bipartite graph)와 같다는 것과 동치임을 관찰하자. 이를 이용해 칠해야 하는 칸들이 이분그래프의 형태로 나타나는지 여부를 이용해 답이 3인지 아닌지를 판정할 수 있다.

 

또한 한 가지 색만으로 주어진 그래프를 칠할 수 있으려면 서로 인접한 칸이 없어야 함을 관찰하자. 이를 이용해 이분그래프를 두 가지 색으로 칠해야하는지 한 가지 색으로 칠해야하는지를 구분지을 수 있다.

 

위의 내용을 구현해 문제를 해결하자.

 

아래는 제출한 소스코드이다.

#include <iostream>
using namespace std;

int N;
string board[50];
int dist[50][50];
int dr[6] = {0,-1,-1,0,1,1};
int dc[6] = {-1,0,1,1,0,-1};
bool chk1, chk2, chk3;
void dfs(int r, int c) {
	for (int i = 0; i < 6; i++) {
		int rr = r + dr[i], cc = c + dc[i];
		if (rr < 0 || rr >= N || cc < 0 || cc >= N || board[rr][cc] == '-') continue;
		if (dist[rr][cc]) {
			if ((dist[r][c] + dist[rr][cc]) % 2 == 0) chk3 = 1;
		}
		else {
			dist[rr][cc] = dist[r][c] + 1;
			dfs(rr, cc);
		}
		chk2 = 1;
	}
}

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

	cin >> N;
	for (int r = 0; r < N; r++) cin >> board[r];

	for (int r = 0; r < N; r++) {
		for (int c = 0; c < N; c++) {
			if (board[r][c] == '-' || dist[r][c]) continue;
			chk1 = 1;
			dist[r][c] = 1;
			dfs(r, c);
		}
	}
	if (chk3) cout << 3;
	else if (chk2) cout << 2;
	else if (chk1) cout << 1;
	else cout << 0;
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 21375 // C++] Konamikoden  (0) 2024.06.27
[BOJ 26092 // C++] 수학적인 최소 공통 조상  (1) 2024.06.26
[BOJ 7587 // C++] Anagrams  (0) 2024.06.24
[BOJ 27738 // C++] 연산자 파티  (0) 2024.06.23
[BOJ 13352 // C++] 석양이 진다...  (0) 2024.06.22

+ Recent posts