※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 1127번 문제인 떡국이다.
문제는 아래 링크를 확인하자.

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

 

1127번: 떡국

떡국의 왕 도현이는 나라를 지키기 위해 각각의 도시에 경비를 뽑으려고 한다. 떡국에는 총 K개의 서로 다른 경비업체가 있으며, 각 경비업체는 일부 도시들에 사무실을 가지고 있다. 어떤 도시

www.acmicpc.net

먼저, 각 경비회사의 경비의 고용은 독립적인 사건이라는 점을 눈치채자. 즉 각 경비회사마다 "충돌"을 최소화해 그 값들을 더하는 것으로 문제를 해결할 수 있다는 점을 관찰하자.

 

경비회사 하나만을 두고 볼 때, 각 도시는 다음 세 가지 상태 중 하나에 속한다:

(1) 경비회사가 들어와있고 경비가 이미 고용되어있다.

(2) 경비회사가 들어와있지만 경비가 고용되어있지는 않다.

(3) 경비회사가 들어와있지 않다.

 

이 때 "충돌"의 수의 최솟값은 도시들의 협력관계 그래프에서 (1) 도시들을 source, (3) 도시들을 sink로 갖는 유량 그래프의 min cut과 같다는 점을 발견하자. max-flow min-cut정리에 의해 최대 유량을 구하는 것으로 min cut은 계산할 수 있다.

 

아래는 제출한 소스코드이다.

#define NODE 52
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
using namespace std;

int ans = 0;
int N, K;
string s[50];
vector<int> guards_of_company[50];
vector<int> cities_of_company[50];
int source, sink;
int edge[NODE][NODE];
vector<int> G[NODE];
int level[NODE];

bool dinic_bfs() { // 현 단계의 level graph 생성; 리턴값: 성공여부
	memset(level, 0, sizeof(level));
	level[source] = 1;
	queue<int> que;
	que.push(source);
	while (!que.empty()) {
		int& cur = que.front();
		for (auto& node : G[cur]) {
			if (edge[cur][node] && level[node] == 0) {
				level[node] = level[cur] + 1;
				que.push(node);
			}
		}
		que.pop();
	}

	return level[sink];
}

int turn[NODE];

int dinic_dfs(int cur, int flow) { // flow값 오버플로우 주의
	if (cur == sink) return flow;
	int cursize = G[cur].size();
	for (int& i = turn[cur]; i < cursize; i++) {
		int& node = G[cur][i];
		if (edge[cur][node] && level[node] == level[cur] + 1) {
			int tmp = dinic_dfs(node, min(edge[cur][node], flow));
			if (tmp) {
				edge[cur][node] -= tmp;
				edge[node][cur] += tmp;
				return tmp;
			}
		}
	}

	return 0;
}

int dinic() { // 실제 maximum flow 계산
	int ret = 0;
	while (dinic_bfs()) {
		memset(turn, 0, sizeof(turn));
		int f;
		while (f = dinic_dfs(source, 1000000007)) ret += f;
	}

	return ret;
}

int arr[52];
void solve(int k) {
	for (auto& v : G) v.clear();
	memset(arr, 0, sizeof(arr));
	memset(edge, 0, sizeof(edge));
	source = N, sink = N + 1;
	for (auto node : guards_of_company[k]) arr[node]++;
	for (auto node : cities_of_company[k]) arr[node]++;
	for (int r = 0; r < N; r++) {
		for (int c = r+1; c < N; c++) {
			if (s[r][c] == '1') {
				if (arr[r] == 0) {
					if (arr[c] == 0) continue;
					else if (arr[c] == 1) {
						edge[c][sink]++;
						G[c].emplace_back(sink);
						G[sink].emplace_back(c);
					}
					else {
						edge[source][sink]++;
						G[source].emplace_back(sink);
						G[sink].emplace_back(source);
					}
				}
				else if (arr[r] == 1) {
					if (arr[c] == 0) {
						edge[r][sink]++;
						G[r].emplace_back(sink);
						G[sink].emplace_back(r);
					}
					else if (arr[c] == 1) {
						edge[r][c]++;
						edge[c][r]++;
						G[r].emplace_back(c);
						G[c].emplace_back(r);
					}
					else {
						edge[source][r]++;
						G[r].emplace_back(source);
						G[source].emplace_back(r);
					}
				}
				else {
					if (arr[c] == 0) {
						edge[source][sink]++;
						G[source].emplace_back(sink);
						G[sink].emplace_back(source);
					}
					else if (arr[c] == 1) {
						edge[source][c]++;
						G[source].emplace_back(c);
						G[c].emplace_back(source);
					}
					else continue;
				}
			}
		}
	}
	ans += dinic();
}

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

	cin >> N;
	for (int r = 0; r < N; r++) cin >> s[r];

	cin >> K;
	for (int n = 0; n < N; n++) {
		int cnt; cin >> cnt;
		while (cnt--) {
			int x; cin >> x;
			guards_of_company[x].emplace_back(n);
		}
	}

	for (int n = 0; n < N; n++) {
		int cnt; cin >> cnt;
		while (cnt--) {
			int x; cin >> x;
			cities_of_company[x].emplace_back(n);
		}
	}

	for (int k = 0; k < K; k++) solve(k);

	cout << ans;
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 8992 // C++] 집기 게임  (0) 2022.08.04
[BOJ 20135 // C++] 연세 마스크 공장  (0) 2022.08.03
[BOJ 2365 // C++] 숫자판 만들기  (0) 2022.08.01
[BOJ 5956 // C++] Symmetry  (0) 2022.07.31
[BOJ 5957 // C++] Cleaning the Dishes  (0) 2022.07.31

+ Recent posts