191019 백준 알고리즘 문제풀기*

|

[17472] 다리 만들기 2

  • 난이도: 상
    • 풀긴 했지만 31% 채점에서 틀렸다고 뜬다.
    • 가능한 모든 test case를 통과했지만 어느부분에서 틀렸는지 모르겠다.
    • 차 후 수정 가능한 test case 발견하면 다시 풀어봐야 할 듯..
  • 문제
    • https://www.acmicpc.net/problem/17472
  • 풀이
    • 입력 받기
    • 섬 번호 붙이기
      • BFS를 이용해서 붙인다.
    • 섬 간에 다리 놓기
      • 특정한 문제의 규칙에 따라 다리를 놓는다.
        • 다리는 점의 한 방향 직선으로만 나간다.
        • 다른 섬을 가로지를 수 없다.
        • 길이는 2 이상이 되어야 한다.
        • 섬 간에는 가장 짧은 경로로 연결된다.
        • 모든 섬은 연결되어있어야 한다.
    • 놓아진 다리 토대로 최소 연결을 확인
      • Union find 알고리즘을 적용한다.
        • 모든 노드를 시작점-끝점-가중치 로 저장하고
        • 가중치를 근거로 sorting한다.
          • 최솟 값이 맨 앞으로 오게끔 설정
        • 다음에 부모 노드를 의미하는 parent 배열을 만들고 자기 자신으로 초기화
        • for문을 돌며 전체 노드 연결 관계들에 대해
          • from과 to의 부모가 같지 않은 경우
          • 결과값에 가중치를 더하고
          • from과 to의 부모를 통일시킨다.
            • to의 부모를 from의 부모로 초기화
        • 위의 일련의 과정을 거친 후, 부모를 한번 더 초기화시킨다.
          • 이는 서로 연결되어있음에도 parent 노드에 부모가 다르게 저장되어 있는 경우를 해결하기 위함임
        • 마지막으로 모든 노드가 연결되어있다면 가중치 합을, 그렇지 않다면 -1을 출력한다.

#pragma warning(disable:4996)
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

#define MAX_ISLAND 7

struct bridge_info
{
	int from, to, length;
};

int map[11][11];
int num_map[11][11];
int check[11][11];
int connect[MAX_ISLAND][MAX_ISLAND];
int parent[MAX_ISLAND];

int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };


vector<pair<int,int>> islands;
vector<bridge_info> bridges;

void make_num(int H, int W)
{
	int num_island = 1;
	for (int i = 0; i<int(islands.size()); i++)
	{
		int cx = islands[i].first;
		int cy = islands[i].second;
		if (num_map[cy][cx] != 0) continue;
		queue<pair<int, int>> q;
		num_map[cy][cx] = num_island;
		q.push(make_pair(cx, cy));
		while (!q.empty())
		{
			cx = q.front().first;
			cy = q.front().second;
			q.pop();
			for (int k = 0; k < 4; k++)
			{
				int nx = cx + dx[k];
				int ny = cy + dy[k];
				if (nx < 0 || W <= nx || ny < 0 || H <= ny) continue;
				if (num_map[ny][nx] != 0) continue;
				if (map[ny][nx] != 1) continue;
				num_map[ny][nx] = num_island;
				q.push(make_pair(nx, ny));
			}
		}
		num_island++;
	}
}

void make_connection(int from, int to, int dist)
{
	int cur_length = connect[from][to];
	if (cur_length == 0)
	{//첫 연결이라면 그냥 연결하고
		connect[from][to] = dist;
		connect[to][from] = dist;
	}
	else
	{//첫 연결이 아니라면 대소비교를 해서 최솟값만 연결한다.
		if (dist < cur_length)
		{//만약 계산된 값이 현재 길이보다 짧다면
			connect[from][to] = dist;
			connect[to][from] = dist;
		}
	}
}

void go(int x, int y, int dir, int W, int H)
{
	memset(&check, 0, sizeof(check));
	int cur_num = num_map[y][x];
	int other_num = -1;
	int bridge_size = -1;
	int cx = x;
	int cy = y;
	check[cy][cx] = 1;
	int sx = x; // 시작 좌표 만들기
	int sy = y;
	while (1)
	{
		int bx = cx; //이전 좌표 만들기
		int by = cy;
		cx += dx[dir];//다음 좌표 만들기
		cy += dy[dir];

		if (cx < 0 || W <= cx || cy < 0 || H <= cy)
		{
			break; // 범위벗어나면 break
		}
		if ((num_map[cy][cx] != 0) && (num_map[cy][cx] == num_map[sy][sx]))
		{
			break; //다리 못 놓고 이전과 같은 섬이여도 break
		}

		check[cy][cx] = check[by][bx] + 1;

    if (num_map[cy][cx] != 0 && num_map[cy][cx] != cur_num)
		{//만약 다른 섬을 만났다면
			other_num = num_map[cy][cx];
			bridge_size = check[by][bx] - 1;
			break;
		}
	}

	if (other_num == -1 || bridge_size <= 1)
	{//해당 방향으로 만들어진 다리로 다른 섬을 찾지 못한 경우
	// 또는 다리 길이가 1인 경우
		return; // 그냥 종료
	}
	else
	{//해당 방향으로 만들어진 다리로 다른 섬을 찾은 경우
		make_connection(cur_num, other_num, bridge_size); // 연결을 만든다.
	}
}

//정렬 함수
bool Compare(const bridge_info &x, const bridge_info &y)
{
	return x.length < y.length;
}
int find(int u)
{
	if (parent[u] == u) return u;
	else return parent[u] = find(parent[u]);
}
void merge(int u, int v)
{
	u = find(u);
	v = find(v);

	if (u == v) return;
	parent[u] = v;
}
void make_bridges(int H, int W)
{
	for (int j = 0; j < H; j++)
	{
		for (int i = 0; i < W; i++)
		{
			if (num_map[j][i] == 0) continue;
			for (int k = 0; k < 4; k++)
			{
				go(i, j, k, W, H);
			}
		}
	}

	//printf("made connections\n");
	//for (int j = 1; j < MAX_ISLAND; j++)
	//{
	//	for (int i = 1; i < MAX_ISLAND; i++)
	//	{
	//		printf("%2d", connect[j][i]);
	//	}
	//	printf("\n");
	//}
	int last_idx = 0;

	//connection 맵에서 모두 연결되는 최소 거리 고르기
	//우선 bridges info 만들기
	for (int j = 1; j < MAX_ISLAND; j++)
	{
		for (int i = 1; i < j; i++)
		{
			if (connect[j][i] == 0) continue;
			bridges.push_back({ j,i,connect[j][i] });
			if (last_idx < j) last_idx = j;
		}
	}
  
	//sort(0번부터 from to length로 연결정보 저장됨
	sort(bridges.begin(), bridges.end(), Compare);
	
	//부모 초기화
	for (int j = 1; j < MAX_ISLAND; j++)
	{
		parent[j] = j;
	}
	int res = 0;
	for (int i = 0; i < bridges.size(); i++)
	{
		if(find(bridges[i].from)!=find(bridges[i].to))
		{
			res += bridges[i].length;

			merge(bridges[i].from, bridges[i].to);
		}
	}

	//부모 한번 더 맞춰주기
	for (int i = 0; i < bridges.size(); i++)
	{
		if (find(bridges[i].from) != find(bridges[i].to))
		{
			merge(bridges[i].from, bridges[i].to);
		}
	}

	/*for (int i = 1; i <= last_idx; i++)
	{
		find(i);
	}*/

	int parent_ = parent[1];
	bool diff_parent = false;
	for (int i = 2; i < last_idx; i++)
	{
		if (parent_ != parent[i]) diff_parent = true;
	}
	if (diff_parent) res = 0;
	if (res == 0) res = -1;
	printf("%d\n", res);
	
}

int main()
{
	int N, M; // H=N, W=M
	scanf("%d %d", &N, &M);
	for (int j = 0; j < N; j++)
	{
		for (int i = 0; i < M; i++)
		{
			scanf("%d", &map[j][i]);
			if (map[j][i] != 0)
			{
				islands.push_back(make_pair(i,j));
			}
		}
	}

	//섬 번호 붙이기
	make_num(N, M);

	//printf("made nums\n");
	//for (int j = 0; j < N; j++)
	//{
	//	for (int i = 0; i < M; i++)
	//	{
	//		printf("%2d", num_map[j][i]);
	//	}
	//	printf("\n");
	//}

	//다리 연결하기
	make_bridges(N, M);

	std::system("pause");
	return 0;
}