티스토리 뷰

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. 결과 사진

 

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

댓글
«   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