📙 Algorithm Solving/Java
📙[Algo] 24.08.01 알고리즘
혜덕hyeduck
2024. 8. 1. 17:30
알고리즘 문제) BOJ 1613. 역사
문제 요약
- 알고 있는 일부 사건들의 전후 관계들이 주어질 때, 주어진 사건들의 전후 관계를 찾아라
시간 제한
- 1초
입력
- 사건의 개수 N, 알고 있는 전후 관계 개수 K
- N : 400이하 자연수
- K : 4만 이하 자연수
- K개 줄에 사건의 전후 관계를 알고 있는 두 사건 번호가 주어짐
- a b
- a는 보다 먼저 발생
- a b
- 사건의 전후 관계를 알고 싶은 사건 쌍의 수 S
- S : 5만 이하 자연수
- S개 줄에 서로 다른 두 사건 번호가 주어짐
출력
- S개 줄에 걸쳐
- 앞에 있는 번호의 사건이 먼저 일어났으면 -1
- 뒤에 있는 번호의 사건이 먼저 일어났으면 1
- 유추할 수 없으면 0을 출력
접근법
- 처음에 위상정렬로 풀려고 했는데, 생각해보니 나중에 알고 싶은 전후 관계 개수를 파악하려면, 위상정렬로 접근하기 어렵다고 느껴졌다.
- 따라서 알고 있는 전후 관계 p c가 주어진다면
- dist[p][c]에 1을 저장했다.
- 즉, p가 c보다 먼저 일어난 경우에 1을 저장했고, 자기 자신은 0, 그 외에는 INF값으로 초기화 했다.
- 이후 플로이드 워셜 알고리즘을 통해
- j사건이 i사건 이후에 발생한다는 게 확실할 경우에만
- i사건 이후 j사건이 발생하기까지 필요한 최소 사건 수를 기록했다.
- j사건이 i사건 이후에 발생한다는 게 확실할 경우에만
- 이후 dist배열을 조회할 경우
- 알고 싶은 사건 i와 사건 j가 주어질 때
- dist[i][j]가 INF 값이 아니라면,
- 무조건 i가 j보다 먼저 일어나는 경우로 -1을 출력했고
- INF값일 경우
- j가 i보다 먼저 일어나는 경우가 확실하지 않다면 dist[j][i]가 INF
- 그게 아니라면 INF가 아닌 양수값일 것이다.
- 이를 구분해 INF값일 경우 추정 불가인 0, 그게 아닐 경우 1을 출력했다.
코드
import java.io.*;
import java.util.*;
public class Main {
static int N, K, S;
static int[][] dist;
static final int INF = 1_000_000_010;
static void floyd() {
for (int k = 0; k < N; k++) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
dist = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (i != j) dist[i][j] = INF;
}
}
int p, c;
for (int i = 0; i < K; i++) {
st = new StringTokenizer(br.readLine());
p = Integer.parseInt(st.nextToken()) - 1;
c = Integer.parseInt(st.nextToken()) - 1;
dist[p][c] = Math.min(dist[p][c], 1);
}
floyd();
S = Integer.parseInt(br.readLine());
int a, b;
StringBuilder sb = new StringBuilder();
while (S-- > 0) {
st = new StringTokenizer(br.readLine());
a = Integer.parseInt(st.nextToken()) - 1;
b = Integer.parseInt(st.nextToken()) - 1;
if (dist[a][b] == INF){
if (dist[b][a] == INF) sb.append(0);
else sb.append(1);
}
else {
sb.append(-1);
}
sb.append("\n");
}
System.out.println(sb);
}
}
알고리즘 문제) BOJ 11404. 플로이드
문제 요약
- N개의 도시와 M개의 버스가 존재
- 출발 도시와 도착 도시의 비용에 관한 버스의 노선이 주어질 때
- 모든 도시쌍에 대해 두 도시간 이동할 때 필요 비용의 최솟값을 출력해라
시간 제한
- 1초
입력
- 도시 개수 N
- 2 ≤ N <+ 100
- 버스 개수 M
- 1 ≤ M ≤ 10만
- M개의 줄에 버스 노선 정보 주어진다
- 출발 도시 a, 도착 도시 b, 비용 c
- a ≠ b
- c : 10만 이하 자연수
- a와 b를 연결하는 노선은 여러 개 존재 가능
출력
- n개의 줄에
- i번째 도시에서 j번째 도시로의 최소 비용을 출력
- 만약 갈 수 없다면 0 출력
접근법
- 모든 정점간의 최소 비용을 구해야하므로 플로이드워셜 알고리즘 사용
- 시간 복잡도 ⇒ O(NNN) = 100100100
코드
import java.io.*;
import java.util.*;
public class Main {
static int N, M;
static int[][] graph;
static final int INF = 1_000_000_010;
static void floyd() {
for (int k = 0; k < N; k++) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
// i -> j로 가능 경우와
// i -> k -> j로 가는 경우 비교
// 최솟값으로 갱신
graph[i][j] = Math.min(graph[i][j], graph[i][k] + graph[k][j]);
}
}
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
M = Integer.parseInt(br.readLine());
graph = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
// 자기 자신으로 연결되는 경우가 아닐 경우 INF로 초기화
if (i != j) graph[i][j] = INF;
}
}
int a, b, c;
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
a = Integer.parseInt(st.nextToken()) - 1;
b = Integer.parseInt(st.nextToken()) - 1;
c = Integer.parseInt(st.nextToken());
// 도로가 여러 개 존재 가능 -> 가장 최소 경로로 갱신
graph[a][b] = Math.min(graph[a][b], c);
}
floyd();
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (graph[i][j] == INF) System.out.print(0 + " ");
else System.out.print(graph[i][j] + " ");
}
System.out.println();
}
}
}