baekjoon

[백준] 9413 제주도 관광

윤만석 2023. 4. 12. 13:13

문제

제주도는 관광객들이 많이 찾는 도시이다. 제주도에는 교차로가 N개, 일방통행 도로 M개가 있다. 각각의 도로는 한 교차로와 다른 교차로를 연결한다. 한 교차로 쌍을 연결하는 도로의 개수가 여러 개일 수 있다.

사실 제주도의 도로는 매우 신기한 형태로 건설되었다. 한 교차로에서 도로를 따라 돌아다니다가 시작한 교차로로 돌아오는 일은 불가능하다. (그럼 제주도에 사는 사람들은 어떻게 출퇴근을 하는 것일까?)

두 그룹이 제주도를 여행할 계획을 세우고 있다. 각 그룹은 한 교차로에서 여행을 시작해 도로를 따라 다니면서 여행을 할 것이다. 하지만, 두 그룹은 사이가 매우 좋지 않기 때문에, 같은 교차로를 공유하면 안 된다. 즉, 두 경로 P1, P2를 구해야 하는데, 각각의 Pi (1 ≤ i ≤ 2)는 한 교차로 si에서 시작해서 ti에서 여행을 마쳐야 하고, 중간에 공유하는 교차로가 있으면 안 된다. 경로의 시작점과 도착점도 겹치면 안 된다. 하지만, Pi가 교차로를 하나만 포함하는 경우는 가능하다. (si = ti)

두 계획을 세우는데, 두 경로에 포함된 교차로 수의 합을 최대로 하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T (1 ≤ T ≤ 10)가 주어진다.

각 테스트 케이스의 첫째 줄에는 교차로의 수 N과 도로의 수 M이 주어진다. (1 ≤ N ≤ 300, 1 ≤ M ≤ 3000) 교차로는 1번부터 N번까지 번호가 매겨져 있다. 다음 M개의 줄에는 도로의 정보를 나타내는 A와 B가 주어지며, A에서 B로 향하는 일방통행 도로라는 뜻이다.

 

출력

각 테스트 케이스마다, 문제의 조건을 만족하면서, 두 경로에 포함된 교차로의 최대 개수를 한 줄에 하나씩 출력한다.

 

 

두 경로를 찾아야하는데, 같은 정점을 통과해서는 안됩니다.

따라서 정점을 분할하는 flow문제가 됩니다.

이때, 두 경로를 통해 가장 많은 정점을 거쳐야 하므로, 정점내에서 V in-V out 비용을 -1로 설정하고,

MCMF문제입니다.

 

소스에서 SPFA를 두번수행하면 됩니다.

#include<bits/stdc++.h>
#define FAST ios_base::sync_with_stdio(false),cin.tie(NULL);
#define mset(v) memset(v,0,sizeof(v));
#define rep(i,a) for(int i=0;i<a;++i)
#define REP(i,a) for(int i=1;i<=a;++i)

using namespace std;

typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int>ti;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
int dy[] = { -1,0,1,0 }, dx[] = { 0,1,0,-1 }, INF = 987654321;

using namespace std;

int TC, N, M, A, B, c[604][604], f[604][604], cost[604][604], ans = 0, S = 601, E = 602;
vi adj[604];
//정점분할 in : 1~V, out:1+N ~ V+N
//S=601,E=602
int MCMF() {
	int ret = 0;
	rep(i, 2) { 
		queue<int>q;
		int p[604], inQ[604], dist[604];
		fill(p, p + 604, -1);
		fill(dist, dist + 604, INF);
		mset(inQ);
		q.push(S);
		dist[S] = 0;
		while (!q.empty()) {
			int cur = q.front();
			q.pop();
			inQ[cur] = 0;
			for (int nxt : adj[cur]) {
				if (dist[nxt] > dist[cur] + cost[cur][nxt] && c[cur][nxt] - f[cur][nxt] > 0) {
					dist[nxt] = dist[cur] + cost[cur][nxt];
					p[nxt] = cur;
					if (!inQ[nxt]) {
						inQ[nxt] = 1;
						q.push(nxt);
					}
				}
			}
		}
		for (int j = E; j != S; j = p[j]) {
			ret += cost[p[j]][j];
			f[p[j]][j] += 1;
			f[j][p[j]] -= 1;
		}
	}
	return ret;
}
int main() {
	FAST;
	cin >> TC;
	while (TC--) {
		cin >> N >> M;
		REP(i, 603)adj[i].clear();
		mset(c);
		mset(f);
		mset(cost);
		ans = 0;
		REP(i, N) {
			c[i][i + N] = 1;
			adj[i].push_back(i + N);

			adj[S].push_back(i);
			adj[i].push_back(S);
			c[S][i] = 1;

			adj[i + N].push_back(E);
			adj[E].push_back(i + N);
			c[i + N][E] = 1;

			cost[i][i + N] = -1;
			cost[i + N][i] = 1;
		}
		while (M--) {
			cin >> A >> B;
			c[A + N][B] = 1;
			adj[A + N].push_back(B);
			adj[B].push_back(A + N);
		}
		cout << -MCMF() << "\n";
	}
}