최소신장트리
: 무향 가중치 그래프에서 신장 트리를 구성하는 간선들의 가중치들의 합이 최소인 신장 트리
정점의 개수가 N일때 N(N-1)/4 -> N^2/4를 기준으로 간선이 많다 적다를 판단한다
간선
중심으로 최소신장트리를 구하는 방법이다
가중치가 작은 간선부터 하나씩 선택하며 정점을 연결한다
O(ElogE)의 시간복잡도를 가진다
싸이클이 생기지 않도록 연결하기 위해 Union Find
알고리즘을 적용하여 사이클 발생 여부를 확인한다
[크루스칼 문제 목록]
BOJ 1197 최소 스패닝 트리
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int V,E;
static long min;
static class Edge implements Comparable<Edge> {
int s;
int e;
int w;
public Edge(int s, int e, int w) {
super();
this.s = s;
this.e = e;
this.w = w;
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.w, o.w);
}
}
static PriorityQueue<Edge> points;
// kruskal
static int[] p;
static int[] r;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
V = Integer.parseInt(st.nextToken());
E = Integer.parseInt(st.nextToken());
points = new PriorityQueue<>();
for(int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int s = Integer.parseInt(st.nextToken());
int e = Integer.parseInt(st.nextToken());
int w = Integer.parseInt(st.nextToken());
points.offer(new Edge(s, e, w));
}
p = new int[V+1];
r = new int[V+1];
makeSet();
int cnt = 0; // V-1회 반복
min=0;
while(cnt!=V-1) {
Edge edge = points.poll();
if(union(edge.s, edge.e)) {
cnt++;
min += edge.w;
}
}
System.out.println(min);
}
static boolean union(int x, int y) {
x=find(x);
y=find(y);
if(x==y) return false; // Cycle 발생
if(r[x]>=r[y]) {
r[x]+=r[y];
p[y]=x;
} else {
r[y]+=r[x];
p[x]=y;
}
return true;
}
static int find(int x) {
if(x==p[x]) return p[x];
else return p[x] = find(p[x]);
}
static void makeSet() {
for(int i = 0; i <= V; i++) {
p[i] = i;
r[i] = 1;
}
}
}
정점
중심으로 최소신장트리를 구하는 방법이다
Priority를 활용하여 임의의 정점에서 인접하면서 아직 선택되지 않은 정점 중에 가장 가중치가 작은 간선을 연결한다
O(ElogV)의 시간복잡도를 가진다
[프림 문제 목록]
BOJ 1197 최소 스패닝 트리
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int V,E;
static Long min;
static class Edge implements Comparable<Edge> {
int v;
int w;
public Edge(int v, int w) {
this.v = v;
this.w = w;
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.w, o.w);
}
}
static PriorityQueue<Edge> points;
// prim
static List<Edge>[] adj;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
V = Integer.parseInt(st.nextToken());
E = Integer.parseInt(st.nextToken());
points = new PriorityQueue<>();
adj = new ArrayList[V+1];
for(int i = 0; i < V+1; i++) {
adj[i]= new ArrayList<>();
}
for(int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int s = Integer.parseInt(st.nextToken());
int e = Integer.parseInt(st.nextToken());
int w = Integer.parseInt(st.nextToken());
adj[s].add(new Edge(e, w));
adj[e].add(new Edge(s, w));
}
System.out.println(prim());
}
static long prim() {
min = 0L;
boolean[] visited = new boolean[V+1]; // prim의 아이디어
points.offer(new Edge(1, 0)); // 임의의 정점 시작 가능
int cnt = 0;
while(!points.isEmpty()) {
Edge edge = points.poll();
if(visited[edge.v]) continue;
min += edge.w;
visited[edge.v] = true;
if(++cnt==V) return min; // cnt == V-1, V-1 모두 찾으면
for(int i = 0; i < adj[edge.v].size(); i++) {
Edge next = adj[edge.v].get(i);
if(visited[next.v]) continue;
points.offer(next);
}
}
return min;
}
}
최단경로
: 간선의 가중치가 있는 그래프에서 두 정점 사이의 경로들중 간선의 가중치의 합이 최소인 경로
다익스트라
: 시작 정점에서의 거리가 최소인 정점을 선택해나가며 경로를 구하는 방식
distance
배열, PriorityQueue
, adj
리스트를 사용한다.
[다익스트라 문제 목록]
BOJ 1753 최단경로
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int V, E, K;
static int[] distance;
static boolean[] visited;
static class Edge implements Comparable<Edge>{
int v;
int w;
public Edge(int v, int w) {
this.v = v;
this.w = w;
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.w, o.w);
}
}
static List<Edge>[] adj;
static int MM=Integer.MAX_VALUE/1000;
static PriorityQueue<Edge> points;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
V=Integer.parseInt(st.nextToken());
E=Integer.parseInt(st.nextToken());
K=Integer.parseInt(br.readLine());
adj=new ArrayList[V];
for(int i = 0; i < V; i++) {
adj[i] = new ArrayList<>();
}
for(int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int s = Integer.parseInt(st.nextToken());
int e = Integer.parseInt(st.nextToken());
int w = Integer.parseInt(st.nextToken());
adj[s-1].add(new Edge(e-1, w));
}
distance = new int[V];
Arrays.fill(distance, MM); // 무한으로 초기화
visited = new boolean[V];
points = new PriorityQueue<>();
points.offer(new Edge(K-1, 0));
distance[K-1]=0; // 시작을 0으로 놓는다!
while(!points.isEmpty()) {
Edge cur = points.poll();
if(visited[cur.v]) continue;
visited[cur.v]=true;
for(Edge nxt : adj[cur.v]) {
if(visited[nxt.v]) continue;
if(distance[nxt.v] > distance[cur.v]+nxt.w) { // 여기만 다르다
distance[nxt.v]= distance[cur.v]+nxt.w;
points.offer(new Edge(nxt.v, distance[nxt.v]));
}
}
}
// k -> i
for(int i = 0; i < V; i++) {
if(distance[i]>=MM) {
System.out.println("INF");
} else {
System.out.println(distance[i]);
}
}
}
}
플로이드 워샬
: 모든 쌍의 최단 경로를 찾는 동적 계획 알고리즘
음의 가중치도 가능하다.
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j])
정점 i,j 사이의 모든 경유지를 탐색해서 그 중 최단 경로를 찾아내는 것이다.
O(N^3) 시간복잡도를 가진다
[플로이드 워샬 문제 목록]
SWEA 1263 사람 네트워크2
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Solution {
static int T;
static int N;
static int[][] adj;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
T = Integer.parseInt(br.readLine());
StringBuilder sb = new StringBuilder();
for(int t = 1; t <= T; t++) {
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
adj = new int[N+1][N+1];
for(int i = 1; i <= N; i++) {
for(int j = 1; j <= N; j++) {
adj[i][j] = Integer.parseInt(st.nextToken());
if(i == j) continue;
if(adj[i][j] == 0) adj[i][j] = 1000;
}
} // 입력 끝
// 플로이드 워샬
for(int k = 1; k <= N; k++) {
for(int i = 1; i <= N; i++) {
for(int j = 1; j <= N; j++) {
if(i == j || i == k || j == k) continue;
adj[i][j] = Math.min(adj[i][j], adj[i][k]+adj[k][j]);
}
}
}
int ans = 5000;
for(int i = 1; i <= N; i++) {
int sum = 0;
for(int j = 1; j <= N; j++) {
sum += adj[i][j];
}
ans = Math.min(ans, sum);
}
sb.append("#"+t+" "+ans+"\n");
}
System.out.println(sb.toString());
}
}