티스토리 뷰

1. 문제 링크

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

 

1867번: 돌멩이 제거

문제 n행 n열의 격자로 나뉜 운동장이 있다. 이 위에 k개의 돌멩이가 있는데, 하나의 돌멩이는 격자 한 칸에 정확히 들어가 있으며, 두 개 이상의 돌멩이가 한 칸에 들어가 있는 경우는 없다. 사고의 위험을 없애기 위해 이 돌멩이를 모두 제거하고 깨끗한 운동장을 만들려고 한다. 돌멩이를 제거할 때에는, 한 행이나 한 열을 따라 직선으로 달려가면서 그 행이나 열에 놓인 돌멩이를 모두 줍는 방식을 쓴다. 여러분이 할 일은 운동장의 상태가 주어졌을 때 최소 몇

www.acmicpc.net

2. 문제 개요

한 번에 하나의 행이나, 하나의 열 중 하나를 골라서 정리할 때, 최소로 움직여서 모든 돌을 치우는 방법.

 

3. 문제 힌트

 최소 버텍스 커버 유형입니다.

 행의 집합, 열의 집합으로 이루어진 그래프는 이분 그래프의 모양을 띄고,

 간선(돌)을 모두 커버 할 수 있는 것.

 

4. 문제 풀이

처음에 문제를 봤을 때 최소 버텍스 커버 유형의 문제일 거라 예상했습니다만,

'간선'이 의미하는 바가 뭔지 명확하게 몰라 풀고 나서도 많이 생각했던 문제였습니다.

 

1번의 실행 단위가 행 or열입니다. 

(문제의 예제로 가정) 9개로 나뉘어있는 한 칸 한 칸을 정점이라 보지 말고

하나의 실행 단위인 행 과열을 정점으로 생각합시다. 그리고 간선을 돌멩이라고 생각해봅시다.

Vertex cover의 의미는 집합 S에서 부분집합 S`를 골랐을 때, 그 집합 S`가 모든 간선을 커버하는가를 의미합니다.

즉, 최소 버텍스 커버는 모든 간선(돌멩이)을 커버하느냐를 의미하고, 모든 돌멩이를 주울 수 있느냐로 바꿀 수 있습니다.

 

따라서 돌멩이가 있는 행, 열( i , j )은 i행 j열 정점을 연결해줍니다.

 

 

 

 

5. 코드

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int ans = 0;
int adj[501][501];

int group[2][501][501];
int groupcount[2] = { 0,0 };

int map[501][501];
int n, k;
bool visited[501];
int p[501];

bool dfs(int cur)
{
	if (visited[cur])
		return false;
	visited[cur] = true;

	for (int next = 0; next < groupcount[1]; ++next)
	{
		if (adj[cur][next] == 1){
			if (p[next] == -1 || dfs(p[next]))
			{
				p[next] = cur;
				return true;
			}
		}
	}
	return false;
}


int main()
{
	scanf("%d %d", &n, &k);

	for (int i = 0; i < k; ++i){
		int y, x;
		scanf("%d %d", &y, &x);
		map[y - 1][x - 1] = 1;
	}
	memset(group, -1, sizeof(group));
	memset(p, -1, sizeof(p));

	//가로 집합만들기
	for (int i = 0; i < n; ++i){
		
		for (int j = 0; j < n; ++j)
		{
			if (map[i][j] == 1)
				group[0][i][j] = groupcount[0];
		}
		groupcount[0]++;
	}

	for (int j = 0; j < n; ++j) {

		for (int i = 0; i < n; ++i)
		{
			if (map[i][j] == 1)
				group[1][i][j] = groupcount[1];
		}
		groupcount[1]++;
	}
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j)
			if (map[i][j] == 1)
				adj[group[0][i][j]][group[1][i][j]] = 1;

	
	for (int i = 0; i < groupcount[0]; ++i)
	{
		memset(visited, false, sizeof(visited));
		if (dfs(i))
			ans++;
	}
	printf("%d", ans);


	return 0;
}

여기서는 인접 행렬을 사용했지만, 인접 리스트를 쓰는 것도 별로 어렵지 않습니다!!

 

6. 결과 사진

 

지적, 댓글 언제나 환영입니다~

댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday