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

 

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

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

 

2472번: 체인점

첫째 줄에는 매장 후보지의 개수를 나타내는 정수 N이 입력된다(1 ≤ N ≤ 100,000). 매장 후보지들은 1부터 N까지의 번호로 구분된다. 둘째 줄에는 아파트 단지의 위치를 나타내는 세 개의 정수 A, B,

www.acmicpc.net

각 장소에서 A, B, C까지의 거리는 dijkstra 알고리즘으로 간단히 구할 수 있다.

 

거리의 값의 범위가 매우 넓으므로 중복값에 유의하여 좌표압축을 해주자.

 

각 장소에서 A까지의 거리들에 중복값이 없고, B와 C에 대해서도 마찬가지였다면 #2336과 같은 문제가 된다.

이 문제에는 중복값이 있으므로, 이 점에 유의하여 구현하자.

 

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

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

struct point {
	int idx, distA, distB, distC;
};

bool compA(point p1, point p2) {
	return p1.distA < p2.distA;
}

bool compB(point p1, point p2) {
	return p1.distB < p2.distB;
}

bool compC(point p1, point p2) {
	return p1.distC < p2.distC;
}

point p[100001];
vector<pair<int, int>> G[100001];
int visited[100001];
int dist[100001];

int seg[262145];

void init(int L, int R, int sI) {
	seg[sI] = 1000000007;
	if (L < R) {
		init(L, (L + R) / 2, sI * 2);
		init((L + R) / 2 + 1, R, sI * 2 + 1);
	}
}

int update(int L, int R, int qI, int qVal, int sI) {
	if (R < qI || qI < L) return seg[sI];
	if (L == R) return seg[sI] = min(seg[sI], qVal);
	return seg[sI] = min(update(L, (L + R) / 2, qI, qVal, sI * 2), update((L + R) / 2 + 1, R, qI, qVal, sI * 2 + 1));
}

int rangemin(int L, int R, int qL, int qR, int sI) {
	if (R < qL || qR < L) return 1000000007;
	if (qL <= L && R <= qR) return seg[sI];
	return min(rangemin(L, (L + R) / 2, qL, qR, sI * 2), rangemin((L + R) / 2 + 1, R, qL, qR, sI * 2 + 1));
}

void dijkstra(int s) {
	priority_queue<pair<int, int>> pq;
	pq.push({ -1,s });
	while (!pq.empty()) {
		int current = pq.top().second, d = -pq.top().first; pq.pop();
		if (visited[current]) continue;
		visited[current] = 1;
		dist[current] = d;
		for (auto node : G[current]) {
			if (visited[node.second]) continue;
			pq.push({ -(d + node.first),node.second });
		}
	}
}

int ans[100001];

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

	int N, A, B, C, M; cin >> N >> A >> B >> C >> M;
	
	while (M--) {
		int x, y, z; cin >> x >> y >> z;
		G[x].push_back({ z,y });
		G[y].push_back({ z,x });
	}

	for (int i = 1; i <= N; i++) p[i].idx = i;

	dijkstra(A);
	for (int i = 1; i <= N; i++) p[i].distA = dist[i];
	memset(visited, 0, sizeof(visited));
	memset(dist, 0, sizeof(dist));
	dijkstra(B);
	for (int i = 1; i <= N; i++) p[i].distB = dist[i];
	memset(visited, 0, sizeof(visited));
	memset(dist, 0, sizeof(dist));
	dijkstra(C);
	for (int i = 1; i <= N; i++) p[i].distC = dist[i];

	int temp = N;
	sort(p + 1, p + N + 1, compA);
	for (int i = N; i > 0; i--) {
		if (p[i].distA == p[i - 1].distA) p[i].distA = temp;
		else p[i].distA = temp--;
	}
	temp = N;
	sort(p + 1, p + N + 1, compB);
	for (int i = N; i > 0; i--) {
		if (p[i].distB == p[i - 1].distB) p[i].distB = temp;
		else p[i].distB = temp--;
	}
	sort(p + 1, p + N + 1, compC);
	
	init(1, N, 1);
	
	queue<pair<int, int>> que;
	for (int i = 1; i <= N; i++) {
		if (p[i - 1].distC != p[i].distC) {
			while (!que.empty()) {
				update(1, N, que.front().first, que.front().second, 1);
				que.pop();
			}
		}
		int x = p[i].distA, y = p[i].distB;
		if (rangemin(1, N, 1, x - 1, 1) < y) continue;
		ans[p[i].idx] = 1;
		que.push({ x,y });
	}

	int Q; cin >> Q;
	while (Q--) {
		int x; cin >> x;
		if (ans[x]) cout << "YES\n";
		else cout << "NO\n";
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 9466 // C++] 텀 프로젝트  (0) 2021.09.27
[BOJ 17609 // C++] 회문  (0) 2021.09.26
[BOJ 1615 // C++] 교차개수세기  (0) 2021.09.24
[BOJ 1017 // C++] 소수 쌍  (0) 2021.09.23
[BOJ 10282 // C++] 해킹  (0) 2021.09.22

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

 

이번에 볼 문제는 백준 1615번 문제인 교차개수세기이다.
문제는 아래 링크를 확인하자.

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

 

1615번: 교차개수세기

첫 줄에 N과 간선의 개수 M이 주어진다. 그 다음 줄부터 M+1번째 줄까지 두 개의 수(i, j)가 주어지는데 이는 왼쪽 그룹의 i번 정점과 오른쪽 그룹의 j번 정점을 연결하는 간선이 있다는 의미이다.

www.acmicpc.net

주어진 선분들의 교차점의 개수가 몇 개인지 세는 문제이다.

선분들을 1) 왼쪽 노드번호가 작은 것부터, 2) 왼쪽 노드번호가 같은 두 선분이 있다면 오른쪽 노드번호가 작은 것부터 살펴보자.

 

지금 보고있는 선분과 앞서 나온 선분들 사이의 교차점의 개수는 지금 긋는 선분의 오른쪽 노드번호보다 큰 오른쪽 노드번호를 갖는 선분들의 수와 같다는 점을 관찰하자. 앞서나온 선분들의 왼쪽끝은 지금의 선분보다 번호가 작기 때문이다. 같은 경우 교차점은 없기 때문에 고려하지 않는다. 2) 규칙을 잘 생각해보면 알 수 있다.

 

따라서 구간합을 관리하는 세그먼트트리(segment tree)를 이용하면 문제를 해결할 수 있다.

 

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

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

int seg[4097];

void update(int L, int R, int qI) {
	int sI = 1;
	while (L < R) {
		seg[sI]++;
		int mid = (L + R) / 2;
		if (qI <= mid) {
			R = mid;
			sI = sI * 2;
		}
		else {
			L = mid + 1;
			sI = sI * 2 + 1;
		}
	}
	seg[sI]++;
}

int rangesum(int L, int R, int qL, int qR, int sI) {
	if (R < qL || qR < L) return 0;
	if (qL <= L && R <= qR)return seg[sI];
	return rangesum(L, (L + R) / 2, qL, qR, sI * 2) + rangesum((L + R) / 2 + 1, R, qL, qR, sI * 2 + 1);
}

vector<pair<int, int>> pairs;

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

	int N, M; cin >> N >> M;
	while (M--) {
		int x, y; cin >> x >> y;
		pairs.push_back({ x,y });
	}

	sort(pairs.begin(), pairs.end());

	ll ans = 0;
	for (auto p : pairs) {
		int x = p.second;
		ans += rangesum(1, N, x + 1, N, 1);
		update(1, N, x);
	}

	cout << ans;
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 17609 // C++] 회문  (0) 2021.09.26
[BOJ 2472 // C++] 체인점  (0) 2021.09.25
[BOJ 1017 // C++] 소수 쌍  (0) 2021.09.23
[BOJ 10282 // C++] 해킹  (0) 2021.09.22
[BOJ 5721 // C++] 사탕 줍기 대회  (0) 2021.09.21

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

 

이번에 볼 문제는 백준 1017번 문제인 소수 쌍이다.
문제는 아래 링크를 확인하자.

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

 

1017번: 소수 쌍

지민이는 수의 리스트가 있을 때, 이를 짝지어 각 쌍의 합이 소수가 되게 하려고 한다. 예를 들어, {1, 4, 7, 10, 11, 12}가 있다고 하자. 지민이는 다음과 같이 그룹지을 수 있다. 1 + 4 = 5, 7 + 10 = 17, 11

www.acmicpc.net

2를 제외한 모든 소수는 홀수이고 서로 다른 두 자연수의 합은 2가 될 수 없다는 점을 눈여겨보자.

두 수의 합이 소수가 되게 하려면 수 하나는 짝수에서, 다른 수 하나는 홀수에서 골라야한다는 점을 알 수 있다.

 

각 수를 노드로 하고, 합이 소수가 되는 두 수(하나는 홀수, 하나는 짝수일 수밖에 없다) 사이에 에지를 그으면 이분그래프(bipartite graph)를 하나 얻을 수 있다. 첫 노드와 이어지는 노드를 하나하나 고정해서 이분매칭을 구해보자.

 

이분매칭의 시간복잡도가 O(VE) = O(N^3)이고 노드를 하나하나 고정하는 경우가 최악의 경우 N가지 있으므로 시간복잡도는 O(N^4)이고, 문제에서 주어진 N의 크기가 50이므로 문제를 해결할 수 있다. (글쓴이는 처음에 이게 통과 안될거라고 잘못 짐작하여 생각을 오래 했다...)

 

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

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

bool sieve[2000];

vector<int> oddnums, evennums;
vector<int> G[1001];
int matching[1001];
int visited[1001];
int fixednum;

int bipartite(int current) {
	if (visited[current]) return 0;
	visited[current] = 1;
	if (current == fixednum) return 0;
	for (auto node : G[current]) {
		if (node == fixednum) continue;
		if (matching[node] == 0) {
			matching[node] = current;
			return 1;
		}
		else if (bipartite(matching[node])) {
			matching[node] = current;
			return 1;
		}
	}
	return 0;
}

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

	sieve[0] = sieve[1] = 1;
	for (int i = 2; i < 44; i++) {
		if (sieve[i]) continue;
		for (int j = i * i; j < 2000; j += i) {
			sieve[j] = 1;
		}
	}

	int N; cin >> N;
	int first; cin >> first;
	for (int i = 1; i < N; i++) {
		int x; cin >> x;
		if (x & 1) oddnums.emplace_back(x);
		else evennums.emplace_back(x);
	}

	for (auto o : oddnums) {
		for (auto e : evennums) {
			if (sieve[o + e]) continue;
			G[o].emplace_back(e);
		}
	}

	vector<int> ans;

	if (first & 1) {
		for (auto e : evennums) {
			if (sieve[first + e]) continue;
			memset(matching, 0, sizeof(matching));
			fixednum = e;
			int cnt = 0;
			for (auto o : oddnums) {
				memset(visited, 0, sizeof(visited));
				cnt += bipartite(o);
			}

			if (cnt + 1 == N / 2) ans.emplace_back(e);
		}
	}
	else {
		for (auto o : oddnums) {
			if (sieve[first + o]) continue;
			memset(matching, 0, sizeof(matching));
			fixednum = o;
			int cnt = 0;
			for (auto o : oddnums) {
				memset(visited, 0, sizeof(visited));
				cnt += bipartite(o);
			}

			if (cnt + 1 == N / 2) ans.emplace_back(o);
		}
	}

	sort(ans.begin(), ans.end());

	if (ans.empty()) cout << -1;
	else {
		for (auto x : ans) cout << x << ' ';
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 2472 // C++] 체인점  (0) 2021.09.25
[BOJ 1615 // C++] 교차개수세기  (0) 2021.09.24
[BOJ 10282 // C++] 해킹  (0) 2021.09.22
[BOJ 5721 // C++] 사탕 줍기 대회  (0) 2021.09.21
[BOJ 22865 // C++] 가장 먼 곳  (0) 2021.09.20

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

 

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

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

 

10282번: 해킹

최흉최악의 해커 yum3이 네트워크 시설의 한 컴퓨터를 해킹했다! 이제 서로에 의존하는 컴퓨터들은 점차 하나둘 전염되기 시작한다. 어떤 컴퓨터 a가 다른 컴퓨터 b에 의존한다면, b가 감염되면

www.acmicpc.net

각 컴퓨터를 노드로, 컴퓨터 사이 의존관계를 가중치를 가진 에지로 생각하자.

 

x가 y를 의존하면 y가 감염되었을 시 일정 시간 후 x를 감염시킨다는 의미이므로, y에서 x방향으로 가는 가중치 s의 에지를 생각할 수 있다.

 

감염된 컴퓨터의 개수는 dijkstra 알고리즘을 돌리는 과정에서 새로운 노드의 거리를 확정지을 때마다 계수하는 것으로 구할 수 있다.

 

가장 마지막에 감염된 컴퓨터는 dijkstra 알고리즘의 특징상 방문 가능한 가장 먼 노드를 마지막에 방문하게 된다는 특징을 이용하여 dijkstra 알고리즘을 돌리면서 등장하는 그때그때의 최단거리를 계속 갱신하는 것으로 구할 수 있다.

 

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

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

priority_queue<pair<int, int>> pq;
vector<pair<int, int>> G[10001];
int dist[10001];
int visited[10001];
int cnt;
int ans;
void dijkstra() {
	while (!pq.empty()) {
		int current = pq.top().second, d = -pq.top().first; pq.pop();
		if (visited[current]) continue;
		visited[current] = 1;
		cnt++; ans = d;
		for (auto node : G[current]) {
			if (visited[node.second]) continue;
			pq.push({ -(d + node.first),node.second });
		}
	}
}

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

	int T; cin >> T;
	while (T--) {
		cnt = 0;
		int N, D, C; cin >> N >> D >> C;
		for (int i = 1; i <= N; i++) G[i].clear();
		memset(dist, 0, sizeof(dist));
		memset(visited, 0, sizeof(visited));
		while (D--) {
			int x, y, s; cin >> x >> y >> s;
			G[y].push_back({ s,x });
		}

		pq.push({ 0,C });
		dijkstra();

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

'BOJ' 카테고리의 다른 글

[BOJ 1615 // C++] 교차개수세기  (0) 2021.09.24
[BOJ 1017 // C++] 소수 쌍  (0) 2021.09.23
[BOJ 5721 // C++] 사탕 줍기 대회  (0) 2021.09.21
[BOJ 22865 // C++] 가장 먼 곳  (0) 2021.09.20
[BOJ 1058 // C++] 친구  (0) 2021.09.19

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

 

이번에 볼 문제는 백준 5721번 문제인 사탕 줍기 대회이다.
문제는 아래 링크를 확인하자.

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

 

5721번: 사탕 줍기 대회

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 M과 N이 주어졌다. (1 ≤ M × N ≤ 105) 다음 M개 줄에는 박스에 들어있는 사탕의 개수 N개가 주어진다. 박스에 들

www.acmicpc.net

문제의 조건을 잘 생각해보면, 하나의 행에서 어떤 사탕을 고른다면 그 행에서 서로 인접하는 칸이 없도록 칸을 골라 최대 개수의 사탕을 주울 수 있다는 점을 알 수 있다.

 

각 행별로 주울 수 있는 최대 개수를 고른다면, 행단위로 "고를 행과 안 고를 행"을 선택하는 것으로 최대로 주울 수 있는 사탕의 개수를 알 수 있다. 방식은 한 행에서 주울 수 있는 최대 사탕의 개수를 구하는 것과 완전히 같다.

 

가능한 행, 열의 개수의 최댓값이 각각 10만이므로 10만 by 10만 크기의 배열을 선언하는 것은 무리이다.

필요한 만큼만 선언하고 계산하는 방식으로 구현하자.

 

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

#include <iostream>
using namespace std;

int cols[100000];
int rows[100000];
int dp[100000];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	int R, C; cin >> R >> C;
	while (R > 0) {
		for (int r = 0; r < R; r++) {
			for (int c = 0; c < C; c++) cin >> cols[c];
			if (C == 1) {
				rows[r] = cols[0];
				continue;
			}
			dp[0] = cols[0], dp[1] = max(cols[0], cols[1]);
			for (int i = 2; i < C; i++) {
				dp[i] = max(dp[i - 1], dp[i - 2] + cols[i]);
			}
			rows[r] = dp[C - 1];
		}
	
		if (R == 1) cout << rows[0] << '\n';
		else {
			dp[0] = rows[0], dp[1] = max(rows[0], rows[1]);
			for (int i = 2; i < R; i++) {
				dp[i] = max(dp[i - 1], dp[i - 2] + rows[i]);
			}
			cout << dp[R - 1] << '\n';
		}

		cin >> R >> C;
	}
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 1017 // C++] 소수 쌍  (0) 2021.09.23
[BOJ 10282 // C++] 해킹  (0) 2021.09.22
[BOJ 22865 // C++] 가장 먼 곳  (0) 2021.09.20
[BOJ 1058 // C++] 친구  (0) 2021.09.19
[BOJ 2661 // C++] 좋은수열  (0) 2021.09.18

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

 

이번에 볼 문제는 백준 22865번 문제인 가장 먼 곳이다.
문제는 아래 링크를 확인하자.

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

 

22865번: 가장 먼 곳

$N$개의 땅 중에서 한 곳에 자취를 하려고 집을 알아보고 있다. 세 명의 친구 $A$, $B$, $C$가 있는데 이 친구들의 살고 있는 집으로부터 가장 먼 곳에 집을 구하려고 한다. 이때, 가장 먼 곳은 선택할

www.acmicpc.net

A, B, C를 거리가 0인 점으로 설정한 뒤, 가장 먼 점을 dijkstra 알고리즘을 이용하여 구하는 것으로 문제를 해결할 수 있다.

 

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

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

vector<pair<int,int>> G[100001];
priority_queue<pair<int, int>> pq;

int ans = 999999;
int ansdist = 0;

int dist[100001];
void dijkstra() {
	while (!pq.empty()) {
		int current = pq.top().second, d = -pq.top().first; pq.pop();
		if (dist[current]) continue;
		dist[current] = d;
		if (d > ansdist) {
			ans = current;
			ansdist = d;
		}
		else if (ansdist == d) {
			if (ans > current) ans = current;
		}
		for (auto node : G[current]) {
			if (!dist[node.second]) {
				pq.push({ -(d + node.first),node.second });
			}
		}
	}
}

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

	int N, A, B, C, M; cin >> N >> A >> B >> C >> M;
	while (M--) {
		int x, y, d; cin >> x >> y >> d;
		G[x].push_back({ d,y });
		G[y].push_back({ d,x });
	}
	
	pq.push({ 1,A });
	pq.push({ 1,B });
	pq.push({ 1,C });
	dijkstra();

	cout << ans;
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 10282 // C++] 해킹  (0) 2021.09.22
[BOJ 5721 // C++] 사탕 줍기 대회  (0) 2021.09.21
[BOJ 1058 // C++] 친구  (0) 2021.09.19
[BOJ 2661 // C++] 좋은수열  (0) 2021.09.18
[BOJ 14891 // C++] 톱니바퀴  (0) 2021.09.17

+ Recent posts