우주신과의 교감_1774번

지니·2021년 1월 25일
0

알고리즘

목록 보기
4/4

문제

황선자씨는 우주신과 교감을 할수 있는 채널러 이다. 하지만 우주신은 하나만 있는 것이 아니기때문에 황선자 씨는 매번 여럿의 우주신과 교감하느라 힘이 든다. 이러던 와중에 새로운 우주신들이 황선자씨를 이용하게 되었다.

하지만 위대한 우주신들은 바로 황선자씨와 연결될 필요가 없다. 이미 황선자씨와 혹은 이미 우주신끼리 교감할 수 있는 우주신들이 있기 때문에 새로운 우주신들은 그 우주신들을 거쳐서 황선자 씨와 교감을 할 수 있다.

우주신들과의 교감은 우주신들과 황선자씨 혹은 우주신들 끼리 이어진 정신적인 통로를 통해 이루어 진다. 하지만 우주신들과 교감하는 것은 힘든 일이기 때문에 황선자씨는 이런 통로들이 긴 것을 좋아하지 않는다. 왜냐하면 통로들이 길 수록 더 힘이 들기 때문이다.

또한 우리들은 3차원 좌표계로 나타낼 수 있는 세상에 살고 있지만 우주신들과 황선자씨는 2차원 좌표계로 나타낼 수 있는 세상에 살고 있다. 통로들의 길이는 2차원 좌표계상의 거리와 같다.

이미 황선자씨와 연결된, 혹은 우주신들과 연결된 통로들이 존재한다. 우리는 황선자 씨를 도와 아직 연결이 되지 않은 우주신들을 연결해 드려야 한다. 새로 만들어야 할 정신적인 통로의 길이들이 합이 최소가 되게 통로를 만들어 “빵상”을 외칠수 있게 도와주자.

우선, 모든 우주신을 연결해야 한다는 부분을 통해 Union-Find 알고리즘을 떠올렸다. 또한, 우주신 간의 거리를 각각 구한 후 이를 기준으로 정렬한 후 작은 거리를 지닌 두 우주신 모두가 기존 집합에 포함되어 있는지를 따져가며 푸는 방향으로 접근했다.

struct LengthInfo {
	int n1;
	int n2;
	double length;

	LengthInfo(int a, int b, double l) {
		n1 = a;
		n2 = b;
		length = l;
	}
};

struct Pos {
	double x;
	double y;

	Pos() {

	}

	Pos(double xVal, double yVal) {
		x = xVal;
		y = yVal;
	}
};

일단 구조체 두 개를 만들어줬다.
LengthInfo : 두 우주신과 그 우주신 간의 거리를 나타냄.
Pos : x좌표, y좌표

priority_queue<LengthInfo, vector<LengthInfo>, Sort> pq;
int parent[1001];
Pos pos[1001];

그리고 두 점 사이의 오름차순으로 정렬해주는 우선순위 큐, 본인의 부모 번호를 담는 parent 배열, x좌표와 y좌표를 담는 pos 배열을 만들었다.

	int n;
	int m;
	cin >> n;
	cin >> m;

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

	for (int i = 0; i < n; i++) {
		int a;
		int b;
		cin >> a;
		cin >> b;

		pos[i + 1] = Pos(a, b);

		for (int j = 1; j <= i; j++) {
			double length = sqrt(pow(a - pos[j].x, 2) + pow(b - pos[j].y, 2));
			pq.push(LengthInfo(j, i + 1, length));
		}
	}

입력 받고 초기화해주는 작업을 해준 후


for (int i = 0; i < m; i++) {
		int a;
		int b;
		cin >> a;
		cin >> b;

		Union(a, b);
	}

문제대로 일부는 합쳐줬다.


int Find(int n) {

	if (parent[n] == n) {
		return n;
	}

	return Find(parent[n]);
}

void Union(int a, int b) {
	parent[Find(a)] = Find(b);
}

Union과 Find 함수도 정의해주고...

	double sum = 0;
	while (!pq.empty()) {
		LengthInfo li = pq.top();
		pq.pop();

		int n1 = li.n1;
		int n2 = li.n2;

		if (Find(n1) != Find(n2)) {
			Union(n1, n2);
			sum += li.length;
		}
	}

정작 문제 푸는 코드는 이게 전부다. 우선순위 큐에 거리 순으로 오름차순으로 정렬된 상태이기 때문에 거리가 작은 순으로 뽑아낼 것이고 그 거리를 이루는 두 번호의 루트값이 같은 값이 아니면(두 번호가 모두 한 그래프 상으로 연결되어 있지 않다면) 한 그래프에 합쳐주고 총 거리에 합산해주는 식으로 짜봤다.

#include <iostream>
#include <queue>
#include <cmath>

using namespace std;

struct LengthInfo {
	int n1;
	int n2;
	double length;

	LengthInfo(int a, int b, double l) {
		n1 = a;
		n2 = b;
		length = l;
	}
};

struct Sort {
	bool operator()(LengthInfo l1, LengthInfo l2) {
		return l1.length > l2.length;
	}
};


struct Pos {
	double x;
	double y;

	Pos() {

	}

	Pos(double xVal, double yVal) {
		x = xVal;
		y = yVal;
	}
};

priority_queue<LengthInfo, vector<LengthInfo>, Sort> pq;
int parent[1001];
Pos pos[1001];

int Find(int n) {

	if (parent[n] == n) {
		return n;
	}

	return Find(parent[n]);
}

void Union(int a, int b) {
	parent[Find(a)] = Find(b);
}

int main() {

	int n;
	int m;
	cin >> n;
	cin >> m;

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

	for (int i = 0; i < n; i++) {
		int a;
		int b;
		cin >> a;
		cin >> b;

		pos[i + 1] = Pos(a, b);

		for (int j = 1; j <= i; j++) {
			double length = sqrt(pow(a - pos[j].x, 2) + pow(b - pos[j].y, 2));
			pq.push(LengthInfo(j, i + 1, length));
		}
	}

	for (int i = 0; i < m; i++) {
		int a;
		int b;
		cin >> a;
		cin >> b;

		Union(a, b);
	}

	double sum = 0;
	while (!pq.empty()) {
		LengthInfo li = pq.top();
		pq.pop();

		int n1 = li.n1;
		int n2 = li.n2;

		if (Find(n1) != Find(n2)) {
			Union(n1, n2);
			sum += li.length;
		}
	}

	cout << fixed;
	cout.precision(2);
	cout.setf(ios::showpoint);
	cout << sum << endl;
	return 0;
}

전체 코드는 이렇다.

profile
duckling

0개의 댓글