4 분 소요

복제 로봇 (백준 Gold 1)

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

로봇을 움직여 맵에 존재하는 모든 Key를 얻는 최소 이동 거리를 구하는 문제

  • 로봇 자가 복제 될 수 있기에
    상하좌우 4방향으로 복제할 수 있다

  • 로봇이 Key를 회수하더라도 딱히 원래 자리로 돌아올 필요는 없음

풀이 방법

이전과 비슷한 '그래프 탐색 + MST' 계열의 문제이다

이전 처럼 푸는 단계를 정해두고 풀어보았다

1. 로봇과 Key의 위치를 각각 저장

탐색에 사용할 정보를 String으로 받은 후
각각의 위치를 저장해두었다

2. 로봇이 Key의 위치를 탐색하는 그래프 탐색 코드 작성

나는 Priority Queue 기반의 BFS 탐색을 사용
(다익스트라와 유사)

  • 이때, 유의할 점은 Key와 Key 사이의 거리 또한 구해야 한다는 점이다
    (Key에서 Key로 이동하는 방법만 가능하거나, 이쪽이 더 저렴한 비용일 수 있음)

3. 이후 만들어진 경로를 통하여 MST를 작성

로봇의 위치를 0번 인덱스
각 Key들에게 임의의 인덱스를 주어
연결 여부와 비용을 확인하는
크루스칼 알고리즘을 사용

모두 연결되었는지를 확인한다

첫 제출 코드

#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>

using namespace std;

int n, m;

struct posInfo
{
	int y, x;
	int idx;
};

int FindParent(vector<int>& pv, int x)
{
	if (pv[x] == x)
		return x;

	return pv[x] = FindParent(pv, pv[x]);
}

bool Union(vector<int>& pv, int a, int b)
{
	a = FindParent(pv, a);
	b = FindParent(pv, b);

	if (a == b)
		return false;

	pv[a] = b;

	return true;
}

struct Edge
{
	int s, t,cost;
};

void FindShortDis(const vector<string>& maps, const posInfo& start, const posInfo& target, vector<Edge>& outEdges)
{
	struct qInfos
	{
		int y, x;
		int cost;
	};

	struct Compare
	{
		bool operator()(qInfos& a, qInfos& b)
		{
			return a.cost > b.cost;
		}
	};

	const int dirY[4] = { -1,0,1,0 };
	const int dirX[4] = { 0,1,0,-1 };

	priority_queue<qInfos, vector<qInfos>, Compare> pq;
	vector<vector<bool>> visit(n, vector<bool>(n, false));
	pq.push({ start.y,start.x,0 });

	while (pq.empty() == false)
	{
		int cy = pq.top().y;
		int cx = pq.top().x;
		int cCost = pq.top().cost;
		pq.pop();

		if (cy < 0 || cy >= n ||
			cx < 0 || cx >= n)
			continue;

		if (maps[cy][cx] == '1')
			continue;

		if (visit[cy][cx])
			continue;

		visit[cy][cx] = true;

		if (cy == target.y &&
			cx == target.x)
		{
			outEdges.emplace_back(Edge{start.idx,target.idx,cCost});
			return;
		}

		for (int i = 0; i < 4; i++)
		{
			pq.push(qInfos{cy + dirY[i],cx + dirX[i],cCost + 1});
		}
	}

}

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

	cin >> n >> m;
	vector<string> maps(n);

	vector<posInfo> keys;
	keys.reserve(m + 1);

	int flag = 1;
	for (int i = 0; i < n; i++)
	{
		cin >> maps[i];
		for (int j = 0; j < n; j++)
		{
			if (maps[i][j] == 'S' ||
				maps[i][j] == 'K')
			{
				posInfo p;
				p.y = i;
				p.x = j;
				p.idx = maps[i][j] == 'S' ? 0 : flag++;
				keys.emplace_back(p);
			}
			
		}
	}

	vector<Edge> sEdges;

	for (int i = 0; i <= m; i++)
	{
		for (int j = i + 1; j <= m; j++)
		{
			FindShortDis(maps, keys[i], keys[j], sEdges);
		}
	}

	sort(sEdges.begin(), sEdges.end(), []
	(const Edge& a, const Edge& b)
		{
			return a.cost < b.cost;
		}
	);

	int answer = 0;
	int kCount = 0;
	vector<int> pv(m + 1);
	for (int i = 0; i <= m; i++)
		pv[i] = i;

	for (auto& e : sEdges)
	{
		if (Union(pv, e.s, e.t))
		{
			answer += e.cost;
			kCount++;
			if (kCount == m)
				break;
		}
	}

	int s = FindParent(pv,0);

	for (int i = 1; i <= m; i++)
	{
		if (s != FindParent(pv, pv[i]))
		{
			cout << -1;
			return 0;
		}
	}

	cout << answer;

	return 0;
}

틀린 이유 1 - 시간초과

모든 Key들에 대하여 ‘일일이’ BFS 탐색을 하였기에
지나치게 느려졌다

따라서 해당 부분을 수정하기 위하여
Map에 Flag를 넣어 인덱스값을 쉽게 가져오려 하였다

다음 제출 코드

...

void FindShortDis(const vector<string>& maps, const posInfo& start, vector<Edge>& outEdges)
{
	...

	char startFlag = maps[start.y][start.x];

	priority_queue<qInfos, vector<qInfos>, Compare> pq;
	vector<vector<bool>> visit(n, vector<bool>(n, false));
	pq.push({ start.y,start.x,0 });

	while (pq.empty() == false)
	{
		int cy = pq.top().y;
		int cx = pq.top().x;
		int cCost = pq.top().cost;
		pq.pop();

		if (cy < 0 || cy >= n ||
			cx < 0 || cx >= n)
			continue;

		if (maps[cy][cx] == '1')
			continue;

		if (visit[cy][cx])
			continue;

		visit[cy][cx] = true;

		if (maps[cy][cx] >= '3' &&
			maps[cy][cx] != startFlag)
		{
			outEdges.emplace_back(Edge{start.idx - 2,(maps[cy][cx] - '0' - 2),cCost});
			continue;
		}

		for (int i = 0; i < 4; i++)
		{
			pq.push(qInfos{cy + dirY[i],cx + dirX[i],cCost + 1});
		}
	}
}

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

	cin >> n >> m;
	vector<string> maps(n);

	vector<posInfo> keys;
	keys.reserve(m + 1);

	int flag = 3;
	for (int i = 0; i < n; i++)
	{
		cin >> maps[i];
		for (int j = 0; j < n; j++)
		{
			if (maps[i][j] == 'S' ||
				maps[i][j] == 'K')
			{
				posInfo p;
				p.y = i;
				p.x = j;
				if (maps[i][j] == 'S')
				{
					maps[i][j] = '2';
					p.idx = 2;
				}
				else
				{
					p.idx = flag;
					maps[i][j] = '0' + flag;
					flag++;
				}
				
				keys.emplace_back(p);
			}
			
		}
	}

	vector<Edge> sEdges;

	for (int i = 0; i <= m; i++)
	{
		FindShortDis(maps, keys[i], sEdges);
	}

	...

	return 0;
}

틀린 이유 2 - M 값은 10 이상이 될 수 있음

Flag를 저렇게 넣고 보니
M은 총 250까지 들어올 있는데
10 이상 부터는 값이 ‘이상’해진다는 문제를 뒤늦게 발견하였다

따라서 그냥 map을 통하여 <위치,idx>를 관리하는 쪽으로 바꾸었다

최종 제출 코드

...

void FindShortDis(const vector<string>& maps, const posInfo& start, vector<Edge>& outEdges, map<pair<int, int>, int>& keyMaps)
{
	...

	priority_queue<qInfos, vector<qInfos>, Compare> pq;
	vector<vector<bool>> visit(n, vector<bool>(n, false));
	pq.push({ start.y,start.x,0 });

	while (pq.empty() == false)
	{
		...

		if (maps[cy][cx] == 'K' &&
			keyMaps[{cy, cx}] != start.idx)
		{
			outEdges.emplace_back(Edge{start.idx,keyMaps[{cy, cx}],cCost});
			continue;
		}

		...
	}
}

int main()
{
	...

	map<pair<int, int>, int> keyMaps;

	int flag = 1;
	for (int i = 0; i < n; i++)
	{
		cin >> maps[i];
		for (int j = 0; j < n; j++)
		{
			if (maps[i][j] == 'S' ||
				maps[i][j] == 'K')
			{
				posInfo p;
				p.y = i;
				p.x = j;
				if (maps[i][j] == 'S')
				{
					p.idx = 0;
				}
				else
				{
					p.idx = flag;
					keyMaps[{i, j}] = flag;
					flag++;
				}
				
				keys.emplace_back(p);
			}
			
		}
	}

	...
}

결과

Image

풀이 과정 자체는 올바르게 잡았지만
세부 구현이 깔끔하지 못하여
시간을 좀 잡아먹었다

댓글남기기