알고리즘 공부/BFS

[백준] 17412 도시왕복하기 1 - 네트워크 플로우 알고리즘(에드몬드 카프 알고리즘)

kdhoooon 2021. 11. 17. 18:15

문제


N개의 도시가 P개의 단방향 길로 연결되어 있다. 이석원은 1번 도시와 2번 도시 사이를 가며 워해머를 한다. 성실한 이석원은 1번에서 2번으로 가는 서로 다른 경로를 최대한 많이 찾으려고 하는데, 이때 한 경로에 포함된 길이 다른 경로에 포함되면 안된다. 입력에는 1번 도시와 2번 도시를 연결하는 길은 없다. 도시의 번호는 1번부터 N번까지이다.

 

 

 

 

풀이


문제가 다소 불친절하게 설명 돼있어서조금 더 부연설명을 하자면,

 

한 경로에 포함된 길이 다른 경로에 포함되면 안된다라는 말은

해당 노드로 들어오는 간선의 수만큼 다른 노드로 갈 수 있다.

이 때 만들 수 있는 경로의 최선의 값을 찾는 것이다.

 

이문제는 네트워크 플로우 알고리즘을 알아야 이해하기 쉽다.

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ndb796&logNo=221237111220 

 

27. 네트워크 플로우(Network Flow)

네트워크 플로우(Network Flow)는 특정한 지점에서 다른 지점으로 데이터가 얼마나 많이 흐르고 있는가를...

blog.naver.com

위 블로그는 네트워크 플로우를 이해하기위해 참고한 블로그다.

 

네트워크 플로우 알고리즘을 이용해서 문제를 풀면

여기서 간선은 최대 값이 1로 동일하다.

따라서 capacity 는 1이다.

 

만약 capacity 값보다 flow 값이 작아 지나갈 수 있는 경로면 그때의 출발 노드를 저장해 둔다. (역 추적하면서 flow 값을 조정할 때 사용하기 위해)

 

<전체코드>

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Main {	
	static int n, p;
	static int[][] capacity; // i -> j 로 가는 간선 용량
	static int[][] flow;     // i -> j 로 현재 흐르고 있는 유량
	static List<Integer>[] map;
	static int[] visited;
	static int answer = 0;
	static StringBuilder sb = new StringBuilder();

	public static int parseInt(String string) {
		return Integer.parseInt(string);
	}
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		n = parseInt(st.nextToken());
		p = parseInt(st.nextToken());
		
		capacity = new int[n + 1][n + 1];
		flow = new int[n + 1][n + 1];
		visited = new int[n + 1];
		map = new List[n + 1];
		for(int i = 1 ; i <= n ; i++) {
			map[i] = new ArrayList<>();
		}
		
		for(int i = 0 ; i < p ; i++) {
			st = new StringTokenizer(br.readLine());
			
			int u = parseInt(st.nextToken());
			int v = parseInt(st.nextToken());
			
			//단방향이지만 역방향 간선도 저장을 해둔다
			map[u].add(v);
			map[v].add(u);
			
			// 용량은 1
			capacity[u][v] = 1;
		}
		
		answer = 0;
		bfs();		
		
		System.out.println(answer);
	}
	
	static void bfs() {
		
		while(true) {
			
			Queue<Integer> queue = new LinkedList<>();
			Arrays.fill(visited, -1);
			queue.offer(1);
			
			while(!queue.isEmpty()) {
				int cur = queue.poll();
				
				for(int next : map[cur]) {
					if(visited[next] != -1) continue;
					
					//용량보다 flow가 작으면 더 흐르게 할 수 있으므로 queue에 추가한다.
					if(capacity[cur][next] > flow[cur][next]) {
						visited[next] = cur;
						if(next == 2) break;
						
						queue.offer(next);
					}
				}				
			}
			
			//더이상 돌아갈 곳이 없다.
			if(visited[2] == -1) break;
			
			for(int i = 2 ; i != 1 ; i = visited[i]) {
				flow[visited[i]][i] += 1;
				flow[i][visited[i]] -= 1;
			}
			
			answer += 1;
		}
	}
}

 


참고

만약 capacity 가 주어지는 문제면 이때의 값을 1이 아닌 더 큰값으로 하면 된다.

for(int i = 2 ; i != 1 ; i = visited[i]) {
	flow[visited[i]][i] += 1;
	flow[i][visited[i]] -= 1;
}

위 행위를 하기 전에 가장 적은 flow 값을 구해줘야 한다.

 

'알고리즘 공부 > BFS' 카테고리의 다른 글

[리트코드] 407. Trapping Rain Water 2 <Java>  (0) 2021.12.03
[백준] 2515 거울설치  (0) 2021.11.17
[백준] 5014 스타트링크  (0) 2021.11.16
[백준] 12886 돌그룹  (0) 2021.08.22
[백준] 1963 소수 경로  (0) 2021.08.22