티스토리 뷰

1. 문제 링크

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

 

10937번: 두부 모판 자르기

KOI 두부 공장에서 만들어내는 크기가 N × N (N ≤ 11)인 두부모판이 있다. 이 모판을 1×1 크기의 단위두부가 2개 붙어있는 형태의 포장단위(즉, 1×2 혹은 2×1 크기)로 잘라서 판매한다. 그런데 두부제조 공정상 모판에 있는 각 단위두부의 품질은 A, B, C, F급으로 분류되고, 잘려진 포장단위의 두부 가격은 이 포장단위에 있는 두 개의 단위두부의 품질에 따라서 그림 1과 같이 정해진다 등급 A B C F A 100 70 40 0 B 70

www.acmicpc.net

2. 문제 개요

 두부를 (1x2) 혹은 (2x1)로 잘라서 상품가치가 가장 높은 가격을 구하는 프로그램 구하기.

 

3. 문제 힌트

   격자무늬 ( 체스판) 모양으로 그룹핑을 해서 이분 그래프로 나타낸다.

(1x2) , (2x1)이므로 인접한 칸과 매핑한다. 그 후 MCMF 알고리즘을 사용하여 최대 매칭의 최대 비용(음수)을 구하는데 조건이 있다.

 

최대 매칭이 꼭 정답이라는 보장이 없다.

 

4. 문제 풀기

격자무늬로 모델링하고 mcmf를 구현한다.

 

그런데 다음과 같은 경우 틀린 답을 출력한다.

4

CAAC

AAAA

AAAA

AAAA

이다.

 

'최대 매칭'을 할 경우 680인 값을 내놓는다 왜냐하면 1행의 CAAC가 AA가 매칭되었다가 CA AC로 분리가 된다. (최대 매칭을 하기 때문)

따라서 매칭을 1번 할때마다 전체 비용이 감소한다면 mcmf를 그만두고 바로 값을 반환해야 한다.

최대 비용이기 때문에 코드에서는 비용을 음수로 두었고 절댓값이 커지는 경우에 mcmf를 탈출하도록 했다.

 

5. 코드

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;

vector<string> v;
//black 1~125, white 126~250,,, src=251, sink=252
const int white = 125, max_size = 253, src = 251, sink = 252, INF = 987654321;
const int dx[] = { 1,-1,0,0 };
const int dy[] = { 0,0,1,-1 };

int cost[max_size][max_size], c[max_size][max_size], f[max_size][max_size];
int numbering[max_size][max_size];
int wnum = 0, bnum = 0;

int mcmf()
{
	int ans = 0;
	int new_ans = 0;

	while (1)
	{
		int p[max_size], d[max_size];
		fill(p, p + max_size, -1);
		fill(d, d + max_size, INF);

		bool inq[max_size] = { false, };
		queue<int> q;

		q.push(src);
		inq[src] = true;
		d[src] = 0;
		p[src] = src;

		while (!q.empty())
		{
			int cur = q.front(); q.pop();
			inq[cur] = false;

			for (int next = 0; next < max_size; ++next)
			{
				if (c[cur][next] - f[cur][next] > 0 && d[next] > d[cur] + cost[cur][next])
				{
					d[next] = d[cur] + cost[cur][next];
					p[next] = cur;
					if (!inq[next])
					{
						q.push(next);
						inq[next] = true;
					}
				}
			}
		}
		if (p[sink] == -1)
			break;

		int minflow = INF;
		for (int i = sink; i != src; i = p[i])
			minflow = min(minflow, c[p[i]][i] - f[p[i]][i]);

		for (int i = sink; i != src; i = p[i])
		{
			new_ans += minflow*cost[p[i]][i];

			f[p[i]][i] += minflow;
			f[i][p[i]] -= minflow;
		}

		if (new_ans <= ans)
			ans = new_ans;
		else
			break;
	}

	return -ans;
}


int cal_cost(char a, char b)
{
	if (a == 'A' && b == 'A')
		return 100;

	if ((a == 'A' && b == 'B') || (a == 'B' && b == 'A'))
		return 70;

	if ((a == 'A' && b == 'C') || (a == 'C' && b == 'A'))
		return 40;

	if (a == 'B' && b == 'B')
		return 50;

	if ((a == 'B' && b == 'C') || (a == 'C' && b == 'B'))
		return 30;

	if (a == 'C' && b == 'C')
		return 20;

	return 0;
}
int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);

	int n;
	cin >> n;
	v.resize(n);

	for (int i = 0; i < n; ++i)
		cin >> v[i];

	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			//black
			if ((i + j) % 2 == 0)
				numbering[i][j] = bnum++;
			else
				numbering[i][j] = white + wnum++;
		}
	}

	//matching
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			if ((i + j) % 2 == 0) {
				for (int dir = 0; dir < 4; ++dir) {
					int ny = i + dy[dir];
					int nx = j + dx[dir];
					if (nx < 0 || ny < 0 || nx >= n || ny >= n)
						continue;
					c[numbering[i][j]][numbering[ny][nx]] = 1;
					cost[numbering[i][j]][numbering[ny][nx]] = -1 * cal_cost(v[i][j], v[ny][nx]);
					cost[numbering[ny][nx]][numbering[i][j]] = cal_cost(v[i][j], v[ny][nx]);
				}
			}
		}
	}

	//src black
	for (int i = 0; i < bnum; ++i)
		c[src][i] = 1;

	//white -> sink
	for (int i = white; i < white + wnum; ++i)
		c[i][sink] = 1;

	printf("%d", mcmf());
	return 0;
}

6. 결과 사진

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

'알고리즘 > MCMF' 카테고리의 다른 글

boj, 백준) 11407. 책 구매하기 3 (C/C++)  (0) 2021.04.12
boj, 백준) 11408. 열혈강호 5 ( C/ C++ )  (0) 2020.03.25
댓글
«   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