문제 : https://www.acmicpc.net/problem/16726
[ 문제 요약 ]
- 직사각형 바닥이 주어질 때, 21 타일과 11타일을 최소한을 놓을 때, 몇 개의 타일을 놓을 수 있는지 구하는 문제입니다. 단, 기둥이 있는 바닥에는 타일을 놓을 수 없습니다.
[ 테스트 케이스 설명 ]
3 4// 행, 열(1<=50)
.X..// X는 기둥, .은 바닥
...X
...X
답 : 5
[ 알고리즘 분류 ]
- 그래프 이론
- 최대 유량
- 이분 매칭
- 격자 그래프
[ 문제 해설 ]
2 *1 과 1 * 1을 사용해 타일을 최소로 놓으려면, 2 * 1을 최대로 사용해야 합니다.
2 * 1을 최대로 사용하는 것은 이분 매칭으로 구할 수 있습니다. 각 타일을 체스판처럼 구분하고, 체스판의 흰색, 또는 검은색 기준으로 왼쪽, 오른쪽 노드로 구분해 인접 리스트를 만들어 최대 매칭을 구하면 됩니다.
답을 구할 때는, 총 빈칸의 합에서, 2 * 1 타일의 수를 빼면 됩니다. 그 이유는 타일의 빈칸의 총합이 A라고 할 때, 2 * 1타일의 개수가 B 이면, A - B2 = 1* 1 타일의 개수가 됩니다. 그럼 모든 타일의 개수는 (A - B*2 + B)가 되어 A-B가 타일의 개수가 됩니다.
[ 정답 코드 ]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
class Main{
static final int dxy[][] = {{1,0},{0,1},{-1,0},{0,-1}};
static int totalBlank;
static int Y, X;
static int idx[][];
static boolean isPos[][];
// 이분 매칭시 사용
static int time;
static int match[];
static int visitTime[];
static List<Integer> adList[];
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
Y = Integer.parseInt(st.nextToken());// 행(1<=50)
X = Integer.parseInt(st.nextToken());// 열(1<=50)
idx = new int[Y + 2][X + 2];
isPos = new boolean[Y + 2][X + 2];
boolean flag;
int i = 0;
int j = 0;
for(int y=1; y<=Y; y++)
{
flag = y % 2 == 1;// 타일을 체스판 처럼 구분하기 위한 값
String str = br.readLine();
for(int x=1; x<=X; x++,flag = !flag)
{
isPos[y][x] = str.charAt(x - 1) == '.';
if(!isPos[y][x])
continue;
++totalBlank;
idx[y][x] = flag ? ++i : ++j;
}
}
match = new int[j + 1];
visitTime = new int[j + 1];
adList = new ArrayList[i + 1];
for(int s=0; s<=i; s++)
adList[s] = new ArrayList<>();
for(int y=1; y<=Y; y++)
{
int x = y % 2 == 1 ? 1 : 2;// 타일 구분을 위해 사용
for(; x<=X; x+=2)
{
if(!isPos[y][x])
continue;
for(int xy[] : dxy)
{
int nextY = y + xy[0];
int nextX = x + xy[1];
if(isPos[nextY][nextX])
adList[idx[y][x]].add(idx[nextY][nextX]);
}
}
}
int cnt = 0;
for(int L=1; L<=i; L++)
{
++time;
if(dfs(L))
++cnt;
}
// 빈칸 총합에서 두칸 짜리 타일 값을 빼면 최종 타일의 개수가 나옴
System.out.print(totalBlank - cnt);
}
static boolean dfs(int L)
{
for(int R : adList[L])
{
if(visitTime[R] == time)
continue;
visitTime[R] = time;
if(match[R] == 0 || dfs(match[R]))
{
match[R] = L;
return true;
}
}
return false;
}
}
'알고리즘 > 이분 매칭' 카테고리의 다른 글
BOJ 백준 3736 System Engineer (0) | 2025.06.03 |
---|---|
BOJ 백준 12843 복수전공 (0) | 2025.06.02 |
BOJ 백준 11670 초등 수학 (0) | 2025.05.30 |
BOJ 백준 2787 흔한 수열 문제 (0) | 2025.05.30 |
BOJ 백준 1760 N-Rook (0) | 2025.05.29 |