티스토리 뷰
1. 문제 링크
https://www.acmicpc.net/problem/11378
11378번: 열혈강호 4
첫째 줄에 직원의 수 N과 일의 개수 M, 지난달에 받은 벌점의 합 K가 주어진다. (1 ≤ N, M ≤ 1,000, 1 ≤ K ≤ N) 둘째 줄부터 N개의 줄의 i번째 줄에는 i번 직원이 할 수 있는 일의 개수와 할 수 있는 일의 번호가 주어진다.
www.acmicpc.net
2. 문제 개요
직원 N명 일 M개가 있다. 벌점을 X점 받은 사람은 일을 최대 X+1개 할 수 있다. 하지만 누가 X점을 받았는지는 모르고 저번 주에 받은 벌점의 합계는 안다. 적절히 배분해야 한다.
N, M, X가 주어 졌을 때 일을 최대 몇 개를 할 수 있는지 구하는 프로그램 작성.
3. 문제 힌트
최대 유량 알고리즘을 사용하기.
edmond karp algorithm.
4. 문제 풀이

문제의 첫 번째 예시를 그림으로 나타내면 위의 그림과 같다.
이번 문제는 이분 탐색으로는 떠오르질 않아 다음과 같이 최대 유량 문제처럼 변경했다.

기본적으로 모두 1개씩 할 수 있으므로 src부터 각 사람(1~5)까지 Link용량이 1인 간선을 연결. 일 M 역시 최대 M개까지 할 수 있으므로 sink와 Link용량이 1인 간선을 연결한다.
이제 기존의 문제와 다른 점은 bridge가 있다는 점이다.
만약에 벌점이 5점이고 1번이 3점, 5번이 2점이라고 한다면, src - bridge는 5의 용량, bridge - 1은 3의 용량, bridge - 5는 2의 용량 이런 식으로 했을 것이다.
하지만 총합만 알고 적절히 나눠가져야 하기 때문에 모두 X만큼 연결해주었다.
이 상태로 그래프를 만들어주고 src를 시작으로 최대 유량 알고리즘을 실행하면 정답을 알 수 있다.
5. 코드
#include <cstdio> #include <queue> #include <vector> #include <algorithm> using namespace std; //source, sink, bridge + 3; vector<int> v[2010]; int n, m, k; int src, dest, bridge; int f[2010][2010]; //flow int c[2010][2010]; //capacity int ans = 0; void edmondkarp() { while (1) { queue<int> q; int p[2010] = { 0 }; // 이전 node fill(p, p + 2010, -1); q.push(src); p[src] = src; while (!q.empty()) { int cur = q.front(); q.pop(); int len = v[cur].size(); for (int i = 0; i < len; ++i) { int next = v[cur][i]; //유량이 남아있고 방문하지 않았으면 if (c[cur][next] - f[cur][next] > 0 && p[next]== -1) { p[next] = cur; q.push(next); if (next == dest) break; } } } if (p[dest] == -1) break; int minflow = 0x7f7f7f7f; for (int i = dest; i != src; i = p[i]) minflow = min(minflow, c[p[i]][i] - f[p[i]][i]); for (int i = dest; i != src; i = p[i]) { f[p[i]][i] += minflow; f[i][p[i]] -= minflow; } ans += minflow; } return; } int main() { scanf("%d %d %d", &n, &m, &k); for (int i = 1; i <= n; ++i) { int t; scanf("%d", &t); for (int j = 0; j < t; ++j) { int val; scanf("%d", &val); v[i].push_back(val+1000); v[val + 1000].push_back(i); c[i][val+1000] = 1; } } src = 2001, dest = 2002, bridge = 2003; //용량은 없어도 경로는 양뱡향이여야함. //src와 bridge connect a line v[src].push_back(bridge); v[bridge].push_back(src); c[src][bridge] = k; //src,bridge와 n연결하기 for (int i = 1; i <= n; ++i) { v[src].push_back(i); v[i].push_back(src); v[bridge].push_back(i); v[i].push_back(bridge); c[src][i] = 1; c[bridge][i] = k; } //m과 sink연결하기 for (int i = 1001; i <= m+1000; ++i) { v[i].push_back(dest); v[dest].push_back(i); c[i][dest] = 1; } edmondkarp(); printf("%d", ans); return 0; }
6. 결과 사진

지적, 댓글 언제나 환영입니다~
'알고리즘 > Network flow' 카테고리의 다른 글
boj, 백준) 2367. 파티 (C/C++) (0) | 2021.07.06 |
---|---|
boj, 백준) 11495. 격자 0 만들기 ( C / C++ ) (0) | 2021.04.24 |
boj, 백준) 11406. 책 구매하기 2 ( C/C++) (0) | 2021.04.09 |
boj, 백준) 17412. 도시 왕복하기1 (C/C++) (0) | 2021.04.07 |
백준, boj) 2316. 도시 왕복하기2 ( C / C++ ) (0) | 2020.03.29 |