문제
농사꾼 존은 소들이 충분한 물을 마시길 원했다. 그래서 농장에서 우물에서 외양간을 잇는 N개의 배수관의 지도를 만들기로 했다. 존은 아주 다양한 크기의 배수관들이 완전히 우연한 방법으로 연결돼있음을 알았다. 존은 파이프를 통과하는 유량을 계산하고 싶다.
두개의 배수관이 한줄로 연결 돼 있을 때 두 관의 유량 중 최솟값으로 흐르게 된다. 예를 들어 용량이 5인 파이프가 용량이 3인 파이프와 연결되면 한개의 용량 3짜리 파이프가 된다.
게다가, 병렬로 연결돼 있는 배수관들은 각 용량의 합만큼의 물을 보낼 수 있다.
마지막으로, 어떤 것에도 연결돼 있지 않은 파이프는 물을 흐르게 하지 못하므로 제거된다.
이로 인해 복잡하게 연결된 모든 배수관들은 한개의 최대 유량을 갖는 배수관으로 만들어진다.
주어진 파이프들의 맵으로부터 우물(A)와 외양간(Z) 사이의 유량을 결정하라.
각 노드의 이름은 알파벳으로 지어져 있다.
파이프 BC와 CD는 합쳐질 수 있다.
그러면 BD와 DZ 역시 합쳐질 수 있다.
병렬 연결된 BZ 역시 합쳐진다.
그러면 AB와 BZ 역시 합쳐질 수 있고 용량 3인 배수관 하나가 만들어진다.
한 파이프들의 집합을 읽고. 두개의 끝점을 가진 파이프로 만들어놓은 뒤 A부터 Z까지 흐르는 최대 유량을 계산하라. 모든 파이프들은 위의 규칙을 적용시켜 줄일 수 있다.
i번째 파이프는 두개의 다른 노드 ai와 bi와 연결돼 있고 Fi(1 ≤ Fi≤ 1,000)만큼의 유량을 갖는다. 알파벳은 같지만, 대소문자가 다르면 다른 문자이다. 파이프는 양방향으로 흐를 수 있다.
입력
첫째 줄에 정수 N (1 ≤ N ≤ 700)이 주어진다. 둘째 줄부터 N+1번째 줄까지 파이프의 정보가 주어진다. 첫 번째, 두 번째 위치에 파이프의 이름(알파벳 대문자 또는 소문자)이 주어지고, 세 번째 위치에 파이프의 용량이 주어진다.
출력
첫째 줄에 A에서 Z까지의 최대 유량을 출력한다.
예제 입력
5
A B 3
B C 3
C D 5
D Z 4
B Z 6
예제 출력
3
접근
정말 하루종일 고생한 문제.. 에드몬드 카프 알고리즘으로 Class 선언해서 BFS로 풀려고 했다. 예제 입력은 통과. 하지만 무슨 이유에서인지 틀린부분이 있었고 결국 포기했다...
풀고싶던 방식의 코드랑 정답이 나오는 코드 둘다 올릴테니 발견한사람은 댓글좀 남겨주세요 ㅠㅠ
코드1(실패)
import java.io.*;
import java.util.*;
class edge {
int to;
int cap;
int flow;
edge rever; // 자신의 역방향을 가리키는 것
edge(){
}
edge(int ar_to, int ar_cap){
this.to = ar_to;
this.cap = ar_cap;
this.flow = 0;
this.rever = null;
}
int residu(){
return this.cap - this.flow;
}
void addflow(int ar_flow){ // 자신과 역방향 간선에 ar_flow만큼의 플로우 값을 갱신
this.flow += ar_flow;
this.rever.flow -= ar_flow;
}
}
public class MaximumFlow_6086 {
static int atoi(String a) {
if(a.charAt(0) <= 'Z') {
return a.charAt(0) - 'A';
} else {
return a.charAt(0) - 'a' + 26;
}
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int[] prev = new int[52];
List<edge>[] adj = new List[52]; // 존재하는 간선만을 인접리스트로 저장
int S = atoi("A");
int E = atoi("Z");
int u=0, v=0, w=0;
int total = 0;
for(int i=0; i<52; i++) {
adj[i] = new ArrayList<edge>();
}
for(int i=0; i<n; i++){
st = new StringTokenizer(br.readLine());
u = atoi(st.nextToken());
v = atoi(st.nextToken());
w = Integer.parseInt(st.nextToken());
edge e1 = new edge(v, w); // 간선
edge e2 = new edge(u, 0); // 역방향 간선
e1.rever = e2;
e2.rever = e1;
adj[u].add(e1);
adj[v].add(e2);
}
while(true) {
edge[] path = new edge[52]; // 경로상의 간선들을 저장해두고 나중에 참조
Arrays.fill(prev, -1);
Queue<Integer> q = new LinkedList<Integer>();
q.add(S);
while(!q.isEmpty() && prev[E] == -1) { // 소스에서 흐를 수 있는 유량 찾기
int curr = q.poll();
int adj_size = adj[curr].size();
for(int i=0; i<adj_size; i++) {
edge next = (edge) adj[curr].get(i);
if(next.residu() > 0 && prev[next.to] == -1) {
q.add(next.to);
prev[next.to] = curr;
path[next.to] = next;
if(next.to == E) break;
}
}
}
if(prev[E] == -1) break; // 경로를 탐색한 후 싱크로 가는 경로가 없는 경우
int flow = Integer.MAX_VALUE;
for(int i=E; i!=S; i=prev[i]) {
flow = Math.min(flow, path[i].residu());
}
for(int i=E; i!=S; i=prev[i]) {
path[i].addflow(flow);
}
total += flow;
}
//System.out.println(total);
bw.write(total+"\n");
bw.flush();
br.close();
bw.close();
}
}
코드2(성공)
import java.util.*;
import java.io.*;
public class Main {
static int[][] capacity, flow;
public static int atoi(char c) {
if('A' <= c && c <= 'Z') return (c - 65);
else if('a'<=c && c<='z') return (c - 71);
return 0;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer token = new StringTokenizer(br.readLine());
// Build Graph
capacity = new int[52][52];
flow = new int[52][52];
int n = Integer.parseInt(token.nextToken());
for(int i=0;i<n;i++){
token = new StringTokenizer(br.readLine());
int start = atoi(token.nextToken().charAt(0));
int end = atoi(token.nextToken().charAt(0));
int weight = Integer.parseInt(token.nextToken());
capacity[start][end] += weight;
capacity[end][start] += weight;
}
int totalFlow = 0;
int source = 0, sink = 25;
while(true) {
int[] parent = new int[52];
Queue<Integer> queue = new PriorityQueue();
for(int i=0;i<52;i++) parent[i] = -1;
parent[source] = source;
queue.add(source);
while(!queue.isEmpty() && parent[sink] == -1) {
int here = queue.poll();
for(int there=0; there<52; there++) {
if (capacity[here][there] - flow[here][there] > 0
&& parent[there] == -1) {
queue.add(there);
parent[there] = here;
}
}
}
// 더이상 증가 경로가 없으면 종료한다.
if (parent[sink] == -1) break;
// 증가 경로를 찾았으면 유량을 결정한다.
int amount = Integer.MAX_VALUE;
for(int i = sink; i!=source; i=parent[i]) {
amount = Math.min(capacity[parent[i]][i] - flow[parent[i]][i], amount);
}
// 증가 경로를 통해 유량을 보낸다.
for(int i = sink; i!=source; i=parent[i]) {
flow[parent[i]][i] += amount;
flow[i][parent[i]] -= amount;
}
totalFlow += amount;
}
bw.write(totalFlow+"\n");
bw.close();
}
}
부들부들
'알고리즘 > [ Baekjoon ]' 카테고리의 다른 글
[ Baekjoon ][JAVA][7812] 중앙 트리 (0) | 2020.10.20 |
---|---|
[ Baekjoon ][JAVA][1708] 볼록껍질 (0) | 2020.09.24 |
[ Baekjoon ][JAVA][2188] 축사배정 (0) | 2020.09.23 |
[ Baekjoon ][JAVA][1789] 수들의 합 (0) | 2020.09.22 |
[ Baekjoon ][JAVA][1365] 꼬인 전깃줄 (0) | 2020.09.21 |