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

 

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

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

 

18266번: Meetings

Two barns are located at positions $0$ and $L$ $(1\le L\le 10^9)$ on a one-dimensional number line. There are also $N$ cows $(1\le N\le 5\cdot 10^4)$ at distinct locations on this number line (think of the barns and cows effectively as points). Each cow $i

www.acmicpc.net

소들의 번호를 생각하지 않고 시간의 흐름에 따른 소의 위치가 어떻게 되는지만을 생각하면 소들의 움직임은 각 소가 처음 보고 있는 방향으로 (다른 소와의 만남에 상관없이) 계속 직진하는 모습과 같다는 점을 관찰하자. 잘 이해가 안된다면 두 소가 만나서 서로 뒤돌아 움직이는 모습과 두 소가 만나고 그대로 지나가는 모습은 번호를 떼고 보면 똑같아보이는 점을 생각해보자.

 

위의 관찰을 이용하면 왼쪽 헛간과 오른쪽 헛간에 (몇번 소인지는 몰라도) 각각 소가 들어오는 시각이 언제일지를 빠르게 구해낼 수 있다.

 

한편, 두 헛간 사이에 있는 소들 중 소 하나가 다음으로 헛간에 들어간다면 그 소는 맨 왼쪽의 소 또는 맨 오른쪽의 소가 될 수밖에 없음을 관찰하자. 중간에 있는 소가 순서를 바꿔 헛간으로 들어갈 수는 없기 때문이다. 이와 먼젓번에서 구한 각 헛간에 소가 들어오는 시각을 이용하면 소들이 헛간에 들어가는 순서를 시간순으로 알 수 있다. 이를 이용해 T값을 구해내자.

 

한편, 좌표 x, y에 있는 두 소가 시간 T 이하에 만난다는 것은 x<y이며 x에 있는 소는 오른쪽을 보고 y에 있는 소는 왼쪽을 바라보고 x+T >= y-T를 만족한다는 것과 같다. 이러한 관계를 만족하는 쌍의 수는 각 소들을 좌표 오름차순으로 살펴보면서 오른쪽을 살펴보고 있는 소들이 들어있는 monotone queue를 관리하는 것으로 빠르게 구해낼 수 있다.

 

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

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

int N; ll L, T;
int arr[50000];
int wgt[50000], total;
ll pos[50000];
int dir[50000];
queue<int> dirque;
ll dist[50000];
int psum, lll, rrr;

ll ans;

bool compD(int& x1, int& x2) {
	return dist[x1] < dist[x2];
}

bool compP(int& x1, int& x2) {
	return pos[x1] < pos[x2];
}

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

	cin >> N >> L;

	for (int i = 0; i < N; i++) {
		arr[i] = i;
		cin >> wgt[i] >> pos[i] >> dir[i];
		total += wgt[i];
		if (dir[i] > 0) dist[i] = L - pos[i];
		else dist[i] = pos[i];
	}
	total = (total + 1) / 2;
	
	sort(arr, arr + N, compD);

	for (int i = 0; i < N; i++) {
		int& idx = arr[i];
		dirque.push(dir[idx]);
	}

	sort(arr, arr + N, compP);

	lll = 0, rrr = N - 1;
	while (psum < total) {
		if (dirque.front() < 0) {
			int& idx = arr[lll];
			psum += wgt[idx];
			lll++;
		}
		else {
			int& idx = arr[rrr];
			psum += wgt[idx];
			rrr--;
		}
		dirque.pop();
	}

	sort(arr, arr + N, compD);

	T = dist[arr[lll + (N - 1 - rrr) - 1]] * 2;

	sort(arr, arr + N, compP);

	queue<ll> que;
	for (int i = 0; i < N; i++) {
		int& idx = arr[i];
		if (dir[idx] > 0) que.push(pos[idx]);
		else {
			while ((!que.empty()) && que.front() + T < pos[idx]) que.pop();
			ans += que.size();
		}
	}

	cout << ans;
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 11999 // C++] Milk Pails (Bronze)  (1) 2023.10.12
[BOJ 18265 // C++] MooBuzz  (0) 2023.10.11
[BOJ 18267 // C++] Milk Visits  (0) 2023.10.09
[BOJ 3188 // C++] nule  (0) 2023.10.08
[BOJ 3185 // C++] kviz  (0) 2023.10.07

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

 

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

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

 

18267번: Milk Visits

Farmer John is planning to build $N$ ($1 \leq N \leq 10^5$) farms that will be connected by $N-1$ roads, forming a tree (i.e., all farms are reachable from each-other, and there are no cycles). Each farm contains a cow, whose breed is either Guernsey or Ho

www.acmicpc.net

소의 품종이 두 종류인 것을 이용해 풀이를 만들어보자.

 

경로의 시점과 종점이 같은 경우에는 풀이가 자명하므로 다른 경우를 생각해보자. (단, 구현시 이에 대해 예외처리를 해야 할 수 있다는 점은 유의하자.)

 

서로 다른 두 노드 A와 B 사이를 잇는 경로에 'G' 품종의 소가 없으려면 A와 B를 잇는 경로의 모든 소의 품종이 'H'와 같음을 의미한다. 이러한 경우 트리의 에지 중 양 끝 노드 모두 'H'품종 소를 키우는 에지만을 이용해 만든 forest에서 A와 B는 항상 같은 component에 들어있음을 관찰할 수 있다.

 

위 관찰은 반대 품종에 대해서도 같은 방식으로 적용할 수 있다.

 

주어진 그래프에서 두 노드가 같은 component에 들어있는지 확인하는 것은 disjoint set 자료구조를 이용해 빠르게 진행할 수 있다. 이를 이용해 문제를 해결하자.

 

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

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

int N, M;
string s;

int ufG[100001];
int findG(int cur) {
	if (ufG[cur] == cur) return cur;
	else return ufG[cur] = findG(ufG[cur]);
}

int ufH[100001];
int findH(int cur) {
	if (ufH[cur] == cur) return cur;
	else return ufH[cur] = findH(ufH[cur]);
}

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

	cin >> N >> M >> s;

	for (int i = 1; i <= N; i++) ufG[i] = i;
	for (int i = 1; i <= N; i++) ufH[i] = i;

	for (int i = 1; i < N; i++) {
		int x, y; cin >> x >> y;
		if (s[x - 1] == s[y - 1]) {
			if (s[x - 1] == 'G') {
				x = findG(x), y = findG(y);
				ufG[y] = x;
			}
			else {
				x = findH(x), y = findH(y);
				ufH[y] = x;
			}
		}
	}

	while (M--) {
		int x, y; char c; cin >> x >> y >> c;
		if (x == y) {
			if (s[x - 1] == c) cout << 1;
			else cout << 0;
		}
		else if (c == 'G') {
			if (findH(x) == findH(y)) cout << 0;
			else cout << 1;
		}
		else {
			if (findG(x) == findG(y)) cout << 0;
			else cout << 1;
		}
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 18265 // C++] MooBuzz  (0) 2023.10.11
[BOJ 18266 // C++] Meetings  (0) 2023.10.10
[BOJ 3188 // C++] nule  (0) 2023.10.08
[BOJ 3185 // C++] kviz  (0) 2023.10.07
[BOJ 11501 // C++] 주식  (1) 2023.10.06

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

 

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

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

 

3188번: nule

We are given a game board consisting of squares arranged in N rows and N columns, with each square containing a single non-negative integer In the beginning of the game, a piece is situated on the top-left square (1,1) and it has to get to the bottom-right

www.acmicpc.net

좌상단의 칸에서 우하단의 칸으로 우측 방향과 아래 방향으로의 인접한, 0이 아닌 칸으로의 이동만을 이용한 경로 중 그 경로상의 칸에 적힌 모든 정수의 곱을 10진수로 나타냈을 때의 trailing zero의 최소 개수를 묻는 문제이다. (좌상단에서 우하단으로 이동이 가능한 입력만 주어짐을 문제가 보장한다.)

 

어떤 경로 T가 trailing zero가 최소가 되게 하는 경로 중 하나라고 해보자. 그리고 이 때 T 위의 모든 칸의 정수를 곱한 수가 소인수로 2를 p개, 5를 q개 가지고 있다고 해보자. 여기서 p<=q라면 모든 경로에 대하여 모든 칸의 정수를 곱한 수가 가지는 소인수 2의 개수가 p 이하여야 하고 마찬가지로 p>=q라면 모든 경로에 대하여 모든 칸의 정수를 곱한 수가 가지는 소인수 5의 개수가 q 이하여야 함을 관찰하자.

 

위의 관찰에 따라, 문제의 답은 좌상단 칸에서 우하단 칸으로 이동하는 경로 중 모든 칸의 정수를 곱한 수가 가지는 (소인수 2의 개수가 최소인 경로의 2의 개수)와 (소인수 5의 개수가 최소인 경로의 5의 개수) 두 값중 더 작은 값임을 알 수 있다.

 

이는 dp를 이용해 \(O(N^2)\)에 계산할 수 있다.

 

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

#include <iostream>
using namespace std;

int N;
int arr[1000][1000];
int dp[1000][1000][2];
int cnt2, cnt5;

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

	cin >> N;
	for (int r = 0; r < N; r++) {
		for (int c = 0; c < N; c++) {
			cin >> arr[r][c];
		}
	}

	while (arr[0][0] % 2 == 0) arr[0][0] /= 2, cnt2++;
	while (arr[0][0] % 5 == 0) arr[0][0] /= 5, cnt5++;
	dp[0][0][0] = cnt2, dp[0][0][1] = cnt5;

	for (int c = 1; c < N; c++) {
		int& arr0c = arr[0][c];
		if (arr0c == 0) dp[0][c][0] = dp[0][c][1] = 1000000007;
		else {
			cnt2 = cnt5 = 0;
			while (arr0c % 2 == 0) arr0c /= 2, cnt2++;
			while (arr0c % 5 == 0) arr0c /= 5, cnt5++;
			dp[0][c][0] = min(dp[0][c - 1][0] + cnt2, 1000000007), dp[0][c][1] = min(dp[0][c - 1][1] + cnt5, 1000000007);
		}
	}

	for (int r = 1; r < N; r++) {
		int& arrr0 = arr[r][0];
		if (arrr0 == 0) dp[r][0][0] = dp[r][0][1] = 1000000007;
		else {
			cnt2 = cnt5 = 0;
			while (arrr0 % 2 == 0) arrr0 /= 2, cnt2++;
			while (arrr0 % 5 == 0) arrr0 /= 5, cnt5++;
			dp[r][0][0] = min(dp[r - 1][0][0] + cnt2, 1000000007), dp[r][0][1] = min(dp[r - 1][0][1] + cnt5, 1000000007);
		}
	}

	for (int r = 1; r < N; r++) {
		for (int c = 1; c < N; c++) {
			int& arrrc = arr[r][c];
			if (arrrc == 0) dp[r][c][0] = dp[r][c][1] = 1000000007;
			else {
				cnt2 = cnt5 = 0;
				while (arrrc % 2 == 0) arrrc /= 2, cnt2++;
				while (arrrc % 5 == 0) arrrc /= 5, cnt5++;
				dp[r][c][0] = min(dp[r - 1][c][0], dp[r][c - 1][0]) + cnt2, dp[r][c][1] = min(dp[r - 1][c][1], dp[r][c - 1][1]) + cnt5;
			}
		}
	}

	cout << min(dp[N - 1][N - 1][0], dp[N - 1][N - 1][1]);
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 18266 // C++] Meetings  (0) 2023.10.10
[BOJ 18267 // C++] Milk Visits  (0) 2023.10.09
[BOJ 3185 // C++] kviz  (0) 2023.10.07
[BOJ 11501 // C++] 주식  (1) 2023.10.06
[BOJ 9079 // C++] 동전 게임  (1) 2023.10.05

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

 

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

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

 

3185번: kviz

In one very popular internet quiz, player has to give the answer to one very hard question. If player does not give the answer after some period of time, the quiz software will give him the first hint, after that the second hint, and in the end the third h

www.acmicpc.net

문제에서 주어진 대로 문자열을 처리해 출력하는 문제이다.

 

이 문제에서 letters란 'A'-'Z', 'a'-'z'를 의미하는 것임에 유의하자. 즉, 먼저 나오는 1/3개의 letters는 알파벳 대소문자 중 먼저 나오는 1/3개의 문자를 의미함에 유의하자.

 

letters가 K개일 때, 앞선 1/3개의 letters는 (K+1)/3, 2/3개의 letters는 (K*2+1)/3으로 계산할 수 있다는 점을 이용하면 반올림 구현을 간단하게 해낼 수 있다. (단, 여기서 '/'는 몫연산이다.)

 

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

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

int slen;
string s;
int lcnt;
bool isvowel[128];
bool vowelleft;

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

	isvowel['a'] = isvowel['e'] = isvowel['i'] = isvowel['o'] = isvowel['u'] = isvowel['A'] = isvowel['E'] = isvowel['I'] = isvowel['O'] = isvowel['U'] = 1;

	getline(cin, s);
	slen = s.length();

	for (auto& l : s) {
		if (('A' <= l && l <= 'Z') || ('a' <= l && l <= 'z')) cout << '.', lcnt++;
		else cout << l;
	}
	cout << '\n';

	for (int i = 0, cnt = 0; i < slen; i++) {
		auto& l = s[i];
		if (('A' <= l && l <= 'Z') || ('a' <= l && l <= 'z')) {
			if (cnt < (lcnt + 1) / 3) cnt++, cout << l;
			else cout << '.';
		}
		else cout << l;
	}
	cout << '\n';

	for (int i = 0, cnt = 0; i < slen; i++) {
		auto& l = s[i];
		if (('A' <= l && l <= 'Z') || ('a' <= l && l <= 'z')) {
			if (cnt < (lcnt + 1) / 3) cnt++;
			else {
				if (isvowel[l]) vowelleft = 1;
			}
		}
	}

	if (vowelleft) {
		for (int i = 0, cnt = 0; i < slen; i++) {
			auto& l = s[i];
			if (('A' <= l && l <= 'Z') || ('a' <= l && l <= 'z')) {
				if (cnt < (lcnt + 1) / 3) cnt++, cout << l;
				else if (isvowel[l]) cout << l;
				else cout << '.';
			}
			else cout << l;
		}
	}
	else {
		for (int i = 0, cnt = 0; i < slen; i++) {
			auto& l = s[i];
			if (('A' <= l && l <= 'Z') || ('a' <= l && l <= 'z')) {
				if (cnt < (lcnt * 2 + 1) / 3) cnt++, cout << l;
				else cout << '.';
			}
			else cout << l;
		}
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 18267 // C++] Milk Visits  (0) 2023.10.09
[BOJ 3188 // C++] nule  (0) 2023.10.08
[BOJ 11501 // C++] 주식  (1) 2023.10.06
[BOJ 9079 // C++] 동전 게임  (1) 2023.10.05
[BOJ 9078 // C++] 정렬  (1) 2023.10.04

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

 

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

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

 

11501번: 주식

입력의 첫 줄에는 테스트케이스 수를 나타내는 자연수 T가 주어진다. 각 테스트케이스 별로 첫 줄에는 날의 수를 나타내는 자연수 N(2 ≤ N ≤ 1,000,000)이 주어지고, 둘째 줄에는 날 별 주가를 나타

www.acmicpc.net

각 날에 대하여 이후의 날 중 그 날보다 주식의 가격이 더 높은 날이 있다면 그 주식을 구입해 앞으로 다가올 날 중 가장 주식이 비싼 날에 판매하는 것이 그 날의 주식으로 얻을 수 있는 최대의 이득임을 관찰하자.

 

한편, 주식을 구입하지 않는 것은 주식을 구입 후 당일에 바로 파는 것과 같게 생각할 수 있음을 관찰하자. 이 관찰을 적용하면 주어진 문제를 각 주식에 대하여 (당일을 포함한) 앞으로 가장 비쌀 가격에 주식을 팔아 얻을 수 있는 총 이득을 계산하는 것으로 해결할 수 있게 된다.

 

해당 날을 포함한 이후 날에 주식의 가장 비싼 가격을 날짜를 시간역순으로 살펴나가 구해나가면서 문제를 해결해주자.

 

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

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

int T, N;
ll arr[1000000];

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

	cin >> T;
	while (T--) {
		ll ans = 0;
		cin >> N;
		for (int i = 0; i < N; i++) cin >> arr[i];
		ll mx = 0;
		for (int i = N - 1; i > -1; i--) {
			mx = max(mx, arr[i]);
			ans += mx - arr[i];
		}

		cout << ans << '\n';
	}
}

 

여담으로, 글쓴이는 처음에는 monotone stack을 이용하여 문제를 해결하였다. 아래는 해당 코드이다.

 

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

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

int T, N;

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

	cin >> T;
	while (T--) {
		cin >> N;
		vector<pair<ll, pair<ll, ll>>> vec; // {주식값, {누적주식값, 누적주식개수}}
		while (N--) {
			ll x; cin >> x;
			ll psum = x, pcnt = 1;
			while (!vec.empty() && vec.back().first < x) {
				psum += vec.back().second.first, pcnt += vec.back().second.second;
				vec.pop_back();
			}
			vec.emplace_back(make_pair(x, make_pair(psum, pcnt)));
		}

		ll ans = 0;
		while (!vec.empty()) {
			ans += vec.back().second.second * vec.back().first - vec.back().second.first;
			vec.pop_back();
		}

		cout << ans << '\n';
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 3188 // C++] nule  (0) 2023.10.08
[BOJ 3185 // C++] kviz  (0) 2023.10.07
[BOJ 9079 // C++] 동전 게임  (1) 2023.10.05
[BOJ 9078 // C++] 정렬  (1) 2023.10.04
[BOJ 2016 // C++] 미팅 주선하기  (0) 2023.10.03

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

 

이번에 볼 문제는 백준 9079번 문제인 동전 게임이다.
문제는 아래 링크를 확인하자.

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

 

9079번: 동전 게임

입력의 첫 줄에는 테스트 케이스의 개수 T(1 ≤ T ≤ 10)가 주어진다. 각 테스트 케이스는 세 줄로 이루어지며, 한 줄에 세 개의 동전모양이 주어지는데, 각각의 동전 표시 사이에는 하나의 공백이

www.acmicpc.net

같은 뒤집을 수 있는 방법을 2회 시행하는 것은 그 시행을 하지 않는 것과 같으므로, 각 뒤집을 수 있는 방법에 대하여 2회 이상 뒤집는 경우는 고려할 필요가 없음을 관찰하자. 즉, 문제의 답이 될 가능성이 있는 있는 시행은 가능한 각 시행에 대하여 그 시행을 하거나 안 하거나의 \(2^8\)가지 경우로 좁힐 수 있다.

 

위의 각 경우를 완전탐색하여 문제를 해결하자. 글쓴이는 각 뒤집는 방법별로 뒤집게 되는 칸을 정리한 flips 배열을 아래와 같이 만들어 완전탐색을 구현하였다.

 

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

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

int T;
int ans, cnt;
pair<int, int> flips[8][3];
int board[3][3];

void func(int idx) {
	if (idx == 8) {
		int cur = board[0][0];
		bool chk = 1;
		for (int r = 0; r < 3; r++) {
			for (int c = 0; c < 3; c++) {
				if (board[r][c] ^ cur) chk = 0;
			}
		}

		if (chk) ans = min(ans, cnt);
	}
	else {
		func(idx + 1);
		for (int i = 0; i < 3; i++) {
			int r = flips[idx][i].first, c = flips[idx][i].second;
			board[r][c] ^= 1;
		}
		cnt++;
		func(idx + 1);
		cnt--;
		for (int i = 0; i < 3; i++) {
			int r = flips[idx][i].first, c = flips[idx][i].second;
			board[r][c] ^= 1;
		}
	}
}

void solve() {
	ans = 1000000007, cnt = 0;
	for (int r = 0; r < 3; r++) {
		for (int c = 0; c < 3; c++) {
			char tmp; cin >> tmp;
			if (tmp == 'H') board[r][c] = 0;
			else board[r][c] = 1;
		}
	}
	func(0);

	if (ans > 1000000000) cout << -1 << '\n';
	else cout << ans << '\n';
}

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

	flips[0][0] = { 0,0 }, flips[0][1] = { 0,1 }, flips[0][2] = { 0,2 };
	flips[1][0] = { 1,0 }, flips[1][1] = { 1,1 }, flips[1][2] = { 1,2 };
	flips[2][0] = { 2,0 }, flips[2][1] = { 2,1 }, flips[2][2] = { 2,2 };
	flips[3][0] = { 0,0 }, flips[3][1] = { 1,0 }, flips[3][2] = { 2,0 };
	flips[4][0] = { 0,1 }, flips[4][1] = { 1,1 }, flips[4][2] = { 2,1 };
	flips[5][0] = { 0,2 }, flips[5][1] = { 1,2 }, flips[5][2] = { 2,2 };
	flips[6][0] = { 0,0 }, flips[6][1] = { 1,1 }, flips[6][2] = { 2,2 };
	flips[7][0] = { 2,0 }, flips[7][1] = { 1,1 }, flips[7][2] = { 0,2 };

	cin >> T;
	while (T--) solve();
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 3185 // C++] kviz  (0) 2023.10.07
[BOJ 11501 // C++] 주식  (1) 2023.10.06
[BOJ 9078 // C++] 정렬  (1) 2023.10.04
[BOJ 2016 // C++] 미팅 주선하기  (0) 2023.10.03
[BOJ 9464 // C++] 직사각형 집합  (1) 2023.10.02

+ Recent posts