반응형

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

 

17472번: 다리 만들기 2

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

www.acmicpc.net

문제풀이

 

1. 섬들을 BFS를 이용해 번호를 붙여준다.

 

2. 번호를 붙여진 섬들간의 간선의 길이를 구한다

 

3. 크루스칼 알고리즘을 이용해 최소스패닝트리의 가중치를 구한다.

 

 

 

 

#include <bits/stdc++.h>
using namespace std;

int unf[7];

int Find(int x) {
	if (x == unf[x]) {
		return x;
	}
	else return unf[x] = Find(unf[x]);
}

void Union(int a, int b) {
	a = Find(a);
	b = Find(b);
	if (a != b) {
		unf[a] = b;
	}
}

struct Edge {
	int x, y, val;
	bool operator<(const Edge& b)const {
		return val > b.val;
	}
};


int n, m;

bool arr_[11][11];
int arr[11][11];
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };
int cnt = 1;

priority_queue<Edge> Kruskal;


void make_island() {
	queue<pair<int,int> > Q;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (arr_[i][j] && arr[i][j] == 0) {
				arr[i][j] = cnt;
				Q.push({ i,j });

				while (!Q.empty()) {
					int x = Q.front().first;
					int y = Q.front().second;
					Q.pop();


					for (int k = 0; k < 4; k++) {
						int nx = x + dx[k];
						int ny = y + dy[k];

						if (nx < 0 || nx >= n || ny < 0 || ny >= m || arr_[nx][ny] == 0 || arr[nx][ny] > 0) continue;
						arr[nx][ny] = cnt;
						Q.push({ nx,ny });
					}
				}
				cnt++;
			}
		}
	}

	return;
}


void make_edge() {
	for (int i = 0; i < n; i++) {
		int start = 0, end = 0, dist = 0;
		for (int j = 0; j < m; j++) {
			if (arr[i][j] != 0) {
				if (start == 0) {
					start = arr[i][j];
					dist = 0;
				}
				else {
					if (arr[i][j] == start) {
						dist = 0;
					}
					else {
						end = arr[i][j];
						if (dist >= 2) Kruskal.push({ start,end,dist });
						start = end;
						end = 0;
						dist = 0;
					}
				}
			}
			else if (arr[i][j] == 0) {
				dist++;
			}
		}
	}

	for (int i = 0; i < m; i++) {
		int start = 0, end = 0, dist = 0;
		for (int j = 0; j < n; j++) {
			if (arr[j][i] != 0) {
				if (start == 0) {
					start = arr[j][i];
					dist = 0;
				}
				else {
					if (arr[j][i] == start) {
						dist = 0;
					}
					else {
						end = arr[j][i];
						if (dist >= 2) Kruskal.push({ start,end,dist });
						start = end;
						end = 0;
						dist = 0;
					}
				}
			}
			else if (arr[j][i] == 0) {
				dist++;
			}
		}
	}

}

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


	for (int i = 0; i < 7; i++) {
		unf[i] = i;
	}

	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> arr_[i][j];
		}
	}
	
	make_island();

	make_edge();

	int ans = 0;
	while (!Kruskal.empty()) {
		int x = Kruskal.top().x;
		int y = Kruskal.top().y;
		int val = Kruskal.top().val;
		Kruskal.pop();

		if (Find(x) != Find(y)) {
			Union(x, y);
			ans += val;
		}
	}

	int comp = Find(1);
	for (int i = 1; i <= cnt-1; i++) {
		if (comp != Find(i)) {
			cout << -1 << '\n';
			return 0;
		}
	}

	cout << ans << '\n';

	return 0;
}

백준 17472번 

반응형
반응형

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

 

2887번: 행성 터널

첫째 줄에 행성의 개수 N이 주어진다. (1 ≤ N ≤ 100,000) 다음 N개 줄에는 각 행성의 x, y, z좌표가 주어진다. 좌표는 -109보다 크거나 같고, 109보다 작거나 같은 정수이다. 한 위치에 행성이 두 개 이

www.acmicpc.net

1.문제설명

두 행성 A(xA, yA, zA)와 B(xB, yB, zB)를 터널로 연결할 때 드는 비용은 min(|xA-xB|, |yA-yB|, |zA-zB|)이다.

INPUT

5
11 -15 -15
14 -5 -15
-1 -1 -5
10 -4 -1
19 -4 19

OUTPUT

4

 

INPUT으로 N개의 행성의 x,y,z 좌표가 주어진다.

 

좌표를 바탕으로 최소 스패닝 트리를 만들어야한다.

 

단 간선의 길이가  min(|xA-xB|, |yA-yB|, |zA-zB|)이다.

 

 

 

 


 

 

 

2. 접근방법[알고리즘]

처음에 간선의 길이가 두 행성간 유클리드 거리인줄 알고 한참 고민했다.

 

이 문제에서 간선의 길이는 x좌표의 차이, y좌표의 차이, z 좌표의 차이 중 최솟값이다.

 

x,y,z 좌표를 각각 입력받고 sort하면 n이 10만이므로 nlogn으로 해결 할 수 있다.

 

그리고 각각 x좌표의 차이 y좌표의 차이 z좌표의 차이를

 

우선순위 큐에 넣고 Union & Find 해주면서

 

크루스칼 알고리즘의 방법으로 최소스패닝트리를 구할 수 있다.

 

 

 

 


 

 

3.문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;

int unf[100001];

int Find(int x) {
	if (x == unf[x]) {
		return x;
	}
	else return unf[x] = Find(unf[x]);
}

void Union(int a, int b) {
	a = Find(a);
	b = Find(b);
	if (a != b) {
		unf[a] = b;
	}
}

struct Edge {
	int x, y, val;
	bool operator<(const Edge& b)const {
		return val > b.val;
	}
};

vector<pair<int, int> > x, y, z;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	for (int i = 0; i < 100001; i++) {
		unf[i] = i;
	}

	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		x.push_back({ a,i });
		y.push_back({ b,i });
		z.push_back({ c,i });
	}

	sort(x.begin(), x.end());
	sort(y.begin(), y.end());
	sort(z.begin(), z.end());

	priority_queue<Edge> Q;

	for (int i = 0; i < n - 1; i++) {
		int dif_x = x[i + 1].first - x[i].first;
		int dif_y = y[i + 1].first - y[i].first;
		int dif_z = z[i + 1].first - z[i].first;
		Q.push({ x[i + 1].second, x[i].second, dif_x });
		Q.push({ y[i + 1].second, y[i].second, dif_y });
		Q.push({ z[i + 1].second, z[i].second, dif_z });
	}

	int ans = 0;
	while (!Q.empty()) {
		int x = Q.top().x;
		int y = Q.top().y;
		int val = Q.top().val;
		Q.pop();

		if (Find(x) != Find(y)) {
			Union(x, y);
			ans += val;
		}
	}

	cout << ans << '\n';

	return 0;
}

 

백준 2887번

 

반응형
반응형

크루스칼 알고리즘과 프림 알고리즘 비교

 

크루스칼 알고리즘은 간선을 기준으로 Spanning Tree를 연결하고

프림 알고리즘은 정점을 기준으로 Spannig Tree를 연결한다.

 

따라서 간선이 많을 경우 프림 알고리즘을

간선이 적을 경우 크루스칼 알고리즘을 이용하는 게 유리하다.

 

 

 


크루스칼 알고리즘

크루스칼 알고리즘의 시간복잡도는 간선을 Sort하는데

최대 O(ElogE)가 필요하다

#include <bits/stdc++.h>
using namespace std;

int unf[10001];

int Find(int x) {
	if (x == unf[x]) {
		return x;
	}
	else return unf[x] = Find(unf[x]);
}

void Union(int a, int b) {
	a = Find(a);
	b = Find(b);
	if (a != b) {
		unf[a] = b;
	}
}

struct Edge {
	int x, y, val;
	bool operator<(const Edge& b)const {
		return val > b.val;
	}
};

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

	for (int i = 0; i < 10001; i++) {
		unf[i] = i;
	}

	priority_queue<Edge> Q;

	int V, E;
	cin >> V >> E;
	for (int i = 0; i < E; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		Q.push({ a,b,c });
	}

	int ans = 0;
	while (!Q.empty()) {
		int x = Q.top().x;
		int y = Q.top().y;
		int val = Q.top().val;
		Q.pop();

		if (Find(x) != Find(y)) {
			Union(x, y);
			ans += val;
		}
	}

	cout << ans << '\n';

	return 0;
}

 

 

프림 알고리즘

프림 알고리즘의 시간 복잡도는 O(E log V)

#include <bits/stdc++.h>
using namespace std;

bool ch[10001];
struct Edge {
	int e;
	int val;
	bool operator<(const Edge& b)const {
		return val > b.val;
	}
};

vector<pair<int, int> > graph[10001];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	priority_queue<Edge> Q;

	int i, n, m, a, b, c, res = 0;
	
	cin >> n >> m;

	for (i = 1; i <= m; i++) {
		cin >> a >> b >> c;
		graph[a].push_back({b,c});
		graph[b].push_back({ a,c });
	}

	Q.push({ 1,0 });
	while (!Q.empty()) {
		Edge tmp = Q.top();
		Q.pop();
		int v = tmp.e;
		int cost = tmp.val;

		if (ch[v] == 0) {
			res += cost;
			ch[v] = 1;
			for (i = 0; i < graph[v].size(); i++) {
				Q.push({ graph[v][i].first, graph[v][i].second });
			}
		}
	}

	cout << res << '\n';
}
반응형
반응형

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

 

1854번: K번째 최단경로 찾기

첫째 줄에 n, m, k가 주어진다. (1 ≤ n ≤ 1000, 0 ≤ m ≤ 2000000, 1 ≤ k ≤ 100) n과 m은 각각 김 조교가 여행을 고려하고 있는 도시들의 개수와, 도시 간에 존재하는 도로의 수이다. 이어지는 m개의 줄에

www.acmicpc.net

1.문제설명

백준 1854번

1번 노드에서 출발하여 다른 모든 노드까지의 K번째 최단 거리를 찾는 문제이다.

 

일반적으로

한 노드에서 다른 모든 노드까지의 최단 거리를 구할 때 다익스트라 알고리즘을 이용한다.

 

이 문제도 다익스트라 알고리즘을 응용하면 K번째 최단거리를 구할 수 있다.

 

 


 

2.접근방법[알고리즘]

 

이 문제에서 가장 헷갈리는 점은 

기존 최단거리를 구하는 다익스트라 알고리즘과 다르게

방문한 노드를 재방문해도 된다는 점이다.

 

방문한 노드를 재방문해도 되도록 코드를 구성하면

계속 무한루프에 빠지지 않을까?

 

이를 해결하기 위해 

방문 여부 대신 다른 조건을 추가해주어야 한다.

 

 

백준 1854번

 

그림에 잘 보면 2->3->5->2가 계속 무한으로 순환할 수 있다.

 

노드를 방문하는 우선순위큐와 다르게

 

특정 노드까지 거리를 저장하는 우선순위큐 배열을 따로 두어서

 

K번째 최단 거리까지 구했다면 더 이상 순환하지 않도록 코드를 구현해야 한다.

 

 

 

 

 

 

 

 

 


 

3.문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;

int n, m, k;
struct Edge {
	int x;
	long long val;
	bool operator<(const Edge& b) const {
		return val > b.val;
	}
};

vector<pair<int, int> > graph[1001];
priority_queue<int> Node[1001];
priority_queue<Edge> Q;

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

	cin >> n >> m >> k;

	for (int i = 0; i < m; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		graph[a].push_back({ b,c });
	}

	Node[1].push(0);
	Q.push({ 1,0 });

	while (!Q.empty()) {
		int x = Q.top().x;
		int val = Q.top().val;
		Q.pop();

		for (int i = 0; i < graph[x].size(); i++) {
			int nx = graph[x][i].first;
			int nd = graph[x][i].second + val;

			if (Node[nx].size() < k) {
				Node[nx].push(nd);
				Q.push({ nx,nd });
			}
			else {
				if (Node[nx].top() > nd) {
					Node[nx].pop();
					Node[nx].push(nd);
					Q.push({ nx,nd });
				}
			}
		}
	}

	for (int i = 1; i <= n; i++) {
		if (Node[i].size() < k) {
			cout << -1 << '\n';
		}
		else {
			cout << Node[i].top() << '\n';
		}
	}


	return 0;
}

백준 1854번

반응형
반응형

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

 

1507번: 궁금한 민호

강호는 N개의 도시로 이루어진 나라에 살고 있다. 각 도시는 M개의 도로로 연결되어 있으며, 각 도로를 지날 때 필요한 시간이 존재한다. 도로는 잘 연결되어 있기 때문에, 도시 A에서 B로 이동할

www.acmicpc.net

1.문제설명

INPUT

5
0 6 15 2 6
6 0 9 8 12
15 9 0 16 18
2 8 16 0 4
6 12 18 4 0

OUTPUT

55

문제에서 N개의 노드가 있을 때

노드에서 노드로 가는 모든 최단 거리를 INPUT으로 준다.

 

 

주어진 노드간 최단 거리를 유지하면서 간선의 개수를 최소로 할 때 

간선의 거리의 합을 구해야 한다.

 

 


2.접근방법[알고리즘]

 

주어진 인풋을 dist배열에 넣고

 

플로이드 워셜을 한번 실행했을 때

 

dist[i][k] + dist[k][j]) == dist[i][j]

이런 상황이 있으면 간선을 최소로 하기위해

dist[i][j] 의 간선을 사용하지 않고

dist[i][k] 와 dist[k][j]의 간선만 사용하면 된다.

 

 

맨 처음엔 문제에서 주어진 인풋의 간선을 모두 사용하는 걸 전제로 하고

위와 같은 간선이 있으면 i - j 를 이어주는 간선을 없애주면

최소 간선을 구해낼 수 있다.

 

for (int k = 0; k < n; k++) {
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				if (i == j || j == k || i == k) continue;
				if ((dist[i][k] + dist[k][j]) == dist[i][j]) {
					arr[i][j] = INF;
				}
				else if (dist[i][k] + dist[k][j] < dist[i][j]) {
					cout << -1 << '\n';
					return 0;
				}
			}
		}
	}

 

 

 

 

 


 

3. 문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;


const int INF = 0x3f3f3f3f;

int n, arr[21][21];

int dist[21][21];
bool ch[21][21];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	memset(dist, 0x3f, sizeof(dist));

	cin >> n;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> arr[i][j];
			dist[i][j] = min(dist[i][j], arr[i][j]);
		}
	}

	for (int k = 0; k < n; k++) {
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				if (i == j || j == k || i == k) continue;
				if ((dist[i][k] + dist[k][j]) == dist[i][j]) {
					arr[i][j] = INF;
				}
				else if (dist[i][k] + dist[k][j] < dist[i][j]) {
					cout << -1 << '\n';
					return 0;
				}
			}
		}
	}

	int sum = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (arr[i][j] < INF) sum += arr[i][j];
		}
	}

	cout << sum / 2 << '\n';



	return 0;
}
반응형
반응형

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

 

1956번: 운동

첫째 줄에 V와 E가 빈칸을 사이에 두고 주어진다. (2 ≤ V ≤ 400, 0 ≤ E ≤ V(V-1)) 다음 E개의 줄에는 각각 세 개의 정수 a, b, c가 주어진다. a번 마을에서 b번 마을로 가는 거리가 c인 도로가 있다는 의

www.acmicpc.net

 

플로이드 와샬 알고리즘을 이용해 풀고

 

거리의 최대치와 INT StackOverflow를 유의해서 풀어야한다.

 

#include <bits/stdc++.h>
using namespace std;

const int INF = 2e9;
int n,m, dist[401][401];

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

	fill(&dist[0][0], &dist[400][401], INF);

	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		dist[a][b] = c;
	}

	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				if (dist[i][k] >= INF || dist[k][j] >= INF) continue;
				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
			}
		}
	}

	int ans = INT_MAX;
	for (int i = 1; i <= n; i++) {
		ans = min(dist[i][i], ans);
	}

	if (ans == INF) {
		cout << -1 << '\n';
	}
	else {
		cout << ans << '\n';
	}
	return 0;
}

백준 1956

반응형
반응형

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

 

2458번: 키 순서

1번부터 N번까지 번호가 붙여져 있는 학생들에 대하여 두 학생끼리 키를 비교한 결과의 일부가 주어져 있다. 단, N명의 학생들의 키는 모두 다르다고 가정한다. 예를 들어, 6명의 학생들에 대하여

www.acmicpc.net

1.문제설명

서로 키를 비교한 결과를 입력으로 주었을 때

 

키의 순서를 알 수있는 노드가 몇 개인지 구하는 문제이다.

 

2.접근방법

해당 노드의 키 순서를 알려면

 

해당 노드를 제외한 모든 노드와 비교가 되어야 한다.

 

플로이드 와샬 알고리즘으로 모든 노드간 거리를 구했을 때

 

만약 거리[a][b] 와 거리[b][a]가 모두 비교가 되지 않은 채 INF 값이 있다면

 

 a와 b는 순서를 알 수 없다.

 

 

 

 

3.문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;



int n, m, dist[501][501];

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

	memset(dist, 0x3f, sizeof(dist));

	cin >> n >> m;

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

	for (int i = 0; i < m; i++) {
		int a, b;
		cin >> a >> b;
		dist[a][b] = 1;
	}


	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
			}
		}
	}

	int ans = 0;
	for (int i = 1; i <= n; i++) {

		bool canKnow = true;
		for (int j = 1; j <= n; j++) {
			if (dist[i][j] > 10000) {
				if (dist[j][i] > 10000) {
					canKnow = false;
				}

			}
		}

		if (canKnow) ans++;

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


	return 0;
}

백준 2458번

 

반응형
반응형

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

 

2610번: 회의준비

첫째 중에 회의에 참석하는 사람의 수 N이 주어진다. 참석자들은 1부터 N까지의 자연수로 표현되며 회의에 참석하는 인원은 100 이하이다. 둘째 줄에는 서로 알고 있는 관계의 수 M이 주어진다. 이

www.acmicpc.net

#include <bits/stdc++.h>
using namespace std;


int n,k;
int dist[101][101];
bool grouped[101];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	memset(dist, 0x3f, sizeof(dist));

	cin >> n >> k;

	for (int i = 0; i < k; i++) {
		int a, b;
		cin >> a >> b;
		dist[a][b] = 1;
		dist[b][a] = 1;
	}

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

	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
			}
		}
	}
	
	int cnt = 0;

	vector<int> king;

	for (int i = 1; i <= n; i++) {
		vector<int> g;
		for (int j = 1; j <= n; j++) {
			if (dist[i][j] < 200 && grouped[j] == false) {
				grouped[j] = true;
				g.push_back(j);
			}
		}
		
		if (g.size() > 0) {
			cnt++;

			int minn = INT_MAX;
			int midx = -1;

			for (int j = 0; j < g.size(); j++) {

				int maxx = 0;
				for (int k = 0; k < g.size(); k++) {
					maxx = max(maxx, dist[g[j]][g[k]]);
				}
				if (maxx < minn) {
					minn = maxx;
					midx = g[j];
				}
			}
			king.push_back(midx);
		}
	}

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

	cout << cnt << '\n';
	for (int i = 0; i < king.size(); i++) {
		cout << king[i] << '\n';
	}

	return 0;
}

백준 2610번

반응형

+ Recent posts