알고리즘 : 그래프, 다익스트라, 그리디, 다이나믹 프로그래밍

 

파티

N개의 숫자로 구분된 각각의 마을에 한 명의 학생이 살고 있다.

어느 날 이 N명의 학생이 X (1 ≤ X ≤ N)번 마을에 모여서 파티를 벌이기로 했다. 이 마을 사이에는 총 M개의 단방향 도로들이 있고 i번째 길을 지나는데 Ti(1 ≤ Ti ≤ 100)의 시간을 소비한다.

각각의 학생들은 파티에 참석하기 위해 걸어가서 다시 그들의 마을로 돌아와야 한다. 하지만 이 학생들은 워낙 게을러서 최단 시간에 오고 가기를 원한다.

이 도로들은 단방향이기 때문에 아마 그들이 오고 가는 길이 다를지도 모른다. N명의 학생들 중 오고 가는데 가장 많은 시간을 소비하는 학생은 누구일지 구하여라.

 

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 10,000), X가 공백으로 구분되어 입력된다. 두 번째 줄부터 M+1번째 줄까지 i번째 도로의 시작점, 끝점, 그리고 이 도로를 지나는데 필요한 소요시간 Ti가 들어온다. 시작점과 끝점이 같은 도로는 없으며, 시작점과 한 도시 A에서 다른 도시 B로 가는 도로의 개수는 최대 1개이다.

모든 학생들은 집에서 X에 갈수 있고, X에서 집으로 돌아올 수 있는 데이터만 입력으로 주어진다.

 

출력

첫 번째 줄에 N명의 학생들 중 오고 가는데 가장 오래 걸리는 학생의 소요시간을 출력한다.

 

예제

예제 입력

4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3

예제 출력

10

 

 


1. 출발 노드를 설정

2. 최단 거리 테이블을 초기화

3. 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택

4. 해당 노드를 거쳐서 다른 노드로 가는 경우를 계산하여 최단 거리 테이블을 갱신

5. 위 과정에서 3~4번을 반복

 

 

import java.io.*;
import java.util.*;


public class Main {
    public static class Node {
        private int index;
        private int distance;
        
        public Node(int idex, int distance) {
            this.index = idex;
            this.distance = distance;
        }
        
        public int getIndex() {
            return this.index;
        }
        
        public int getDistance() {
            return this.distance;
        }
    
    }

    private static final int INF = (int) 1e9; // 무한을 의미하는 값으로 10억을 설정
    private static final int MAX = 1010;
     
    private static int N, M, X;
  
    private static ArrayList<ArrayList<Node>> g = new ArrayList<ArrayList<Node>>();
    private static ArrayList<ArrayList<Node>> rg = new ArrayList<ArrayList<Node>>();
    private static boolean[] visited = new boolean[MAX];
    
    // 고정된 노드에서 to 까지의 거리
    private static int[] d = new int[MAX];
    
    // 방문하지 않은 노드 중에서, 가장 최단 거리가 짧은 노드의 번호를 반환
    private static int getSmallestNode() {
        int min = INF;
        int idx = 1;
        for (int i=1;i<=N;i++) {
            if(!visited[i] && d[i] < min) {
                min = d[i];
                idx = i;
            }
        }
        return idx;
    }
    
    private static void dijkstra(int start) {
        d[start] = 0;
        visited[start] = true;
        for(Node node: g.get(start-1)) {
            d[node.getIndex()] = node.getDistance();
        }
        
        for(int i=1;i<= N-1; i++) {
            int idx = getSmallestNode();
            visited[idx] = true;
            for(Node node: g.get(idx-1)) {
                int cur = d[idx] + node.getDistance();
                if(cur < d[node.getIndex()]) {
                    d[node.getIndex()] = cur;
                }
            }
        }
    }
    
    
    
    public static void main(String args[]) throws Exception {
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      StringTokenizer st = new StringTokenizer(br.readLine());
      N = Integer.parseInt(st.nextToken());
      M = Integer.parseInt(st.nextToken());
      X = Integer.parseInt(st.nextToken());
      
      for(int i=1;i<=N;i++) {
          g.add(new ArrayList<Node>());
          rg.add(new ArrayList<Node>());
      }
      
      for(int i=1;i<=M;i++) {
          st = new StringTokenizer(br.readLine());
          int from = Integer.parseInt(st.nextToken());
          int to = Integer.parseInt(st.nextToken());
          int distance = Integer.parseInt(st.nextToken());
          Node node = new Node(to, distance);
          g.get(from-1).add(node);
      }
      
      int[] toTarget = new int[N+1];
      // 목표 지점까지 가는 최단 거리
      for(int i=1;i<=N;i++) {
          if(i != X) {
               Arrays.fill(d, INF);
               Arrays.fill(visited, false);
               dijkstra(i);
               toTarget[i] = d[X];
          }
      }
      
      Arrays.fill(d, INF);
      Arrays.fill(visited, false);
      // 목표 지점에서 각 마을 까지 돌아가는 최단거리
      dijkstra(X);
      int max = 0;
      
      // 목표 지점까지 오고 가는데 가장 많은 소요 시간 구하기.
      for(int i=1;i<=N;i++) {
          if(i != X) {
              int sum = toTarget[i] + d[i];
              if(sum > max) {
                  max = sum;
              }
          }
      }
      
      System.out.println(max);
      
    }
}

 

 

 

참조

- 다익스트라 알고리즘 : https://www.youtube.com/watch?v=611B-9zk2o4 

- 다익스트라 알고리즘 : https://www.youtube.com/watch?v=F-tkqjUiik0 

- 백준 1238번 : https://www.acmicpc.net/problem/1238

알고리즘 : SCC

Strongly Connected Component

시간 제한 메모리 제한
2초 512 MB

 

 

문제

방향 그래프가 주어졌을 때, 그 그래프를 SCC들로 나누는 프로그램을 작성하시오.

방향 그래프의 SCC는 우선 정점의 최대 부분집합이며, 그 부분집합에 들어있는 서로 다른 임의의 두 정점 u, v에 대해서 u에서 v로 가는 경로와 v에서 u로 가는 경로가 모두 존재하는 경우를 말한다.

예를 들어 위와 같은 그림을 보자. 이 그래프에서 SCC들은 {a, b, e}, {c, d}, {f, g}, {h} 가 있다. 물론 h에서 h로 가는 간선이 없는 경우에도 {h}는 SCC를 이룬다.

 

입력

첫째 줄에 두 정수 V(1 ≤ V ≤ 10,000), E(1 ≤ E ≤ 100,000)가 주어진다. 이는 그래프가 V개의 정점과 E개의 간선으로 이루어져 있다는 의미이다. 다음 E개의 줄에는 간선에 대한 정보를 나타내는 두 정수 A, B가 주어진다. 이는 A번 정점과 B번 정점이 연결되어 있다는 의미이다. 이때 방향은 A → B가 된다.

정점은 1부터 V까지 번호가 매겨져 있다.

 

출력

첫째 줄에 SCC의 개수 K를 출력한다. 다음 K개의 줄에는 각 줄에 하나의 SCC에 속한 정점의 번호를 출력한다. 각 줄의 끝에는 -1을 출력하여 그 줄의 끝을 나타낸다. 각각의 SCC를 출력할 때 그 안에 속한 정점들은 오름차순으로 출력한다. 또한 여러 개의 SCC에 대해서는 그 안에 속해있는 가장 작은 정점의 정점 번호 순으로 출력한다.

 

 

예제 입력 1

7 9
1 4
4 5
5 1
1 6
6 7
2 7
7 3
3 7
7 2

 

예제 입력 2

3
1 4 5 -1
2 3 7 -1
6 -1

 

풀이

SCC : https://www.youtube.com/watch?v=M9d_DthAxwg 

 

import java.io.*;
import java.util.*;

public class Main {
    static ArrayList<Integer>[] route;
    static ArrayList<Integer>[] reverseRoute;
    static ArrayList<ArrayList<Integer>> answerList = new ArrayList<>();
    
    static boolean[] visited;
    static int[] numCnts;
    
    static int numCnt = 1;
    
    private static void dfs(int cur) {
        for(int next: route[cur]) {
            if(!visited[next]) {
                visited[next] = true;
                dfs(next);
            }
        }
        numCnts[numCnt++] = cur;
    }
    
    private static void dfs2(int cur) {
        answerList.get(answerList.size() - 1).add(cur);
        for(int next: reverseRoute[cur]) {
            if(!visited[next]) {
                visited[next] = true;
                dfs2(next);
            }
        }
    }
    
    @SuppressWarnings("unchecked")
    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        
        st = new StringTokenizer(br.readLine());
        int V = Integer.parseInt(st.nextToken());
        int E = Integer.parseInt(st.nextToken());
        
        route = new ArrayList[V+1];
        reverseRoute = new ArrayList[V+1];
        
        for(int i = 1; i <= V; i++) {
            route[i] = new ArrayList<>();
            reverseRoute[i] = new ArrayList<>();
        }
        
        visited = new boolean[V+1];
        numCnts = new int[V+1];

        for(int i=1; i <= E; i++) {
            st = new StringTokenizer(br.readLine());
            int from = Integer.parseInt(st.nextToken());
            int to = Integer.parseInt(st.nextToken());
            route[from].add(to);
            reverseRoute[to].add(from);
        }
        
        // DFS를 하면서, 빠져나오는 순서대로 시간을 기록
        for(int i=1; i <= V; i++) {
            if(!visited[i]) {
                visited[i] = true;
                dfs(i);
            }
        }
        
        visited = new boolean[V+1];
        
        // 뒤집은 그래프에 대해서, 빠져나오는 시간이 큰 노느부터 순회
        // 이 때 만나는 노드들이 모두 같은 그룹임.
        for(int i=V; i>= 1; i--) {
            if(!visited[numCnts[i]]) {
                visited[numCnts[i]] = true;
                answerList.add(new ArrayList<Integer>());
                dfs2(numCnts[i]);
            }
        }
        
        for(ArrayList<Integer> next: answerList) {
            Collections.sort(next);
        }
        
        answerList.sort((o1, o2) -> o1.get(0) - o2.get(0));
        
        System.out.println(answerList.size());
        for(int i=0; i < answerList.size(); i++) {
            for(int next: answerList.get(i)) {
                System.out.print(next + " ");
            }
            System.out.println("-1");
        }
    }
}

출처

'알고리즘' 카테고리의 다른 글

BFS/미로 탈출 백준 14923번  (2) 2023.10.28
그래프/파티 백준 1238번  (0) 2023.10.04
DP/1,2,3 더하기 백준 9095번  (0) 2022.10.09
DFS/웜바이러스 백준 2606번  (0) 2022.02.28
스택/괄호의값 백준 2504번  (0) 2022.02.13
알고리즘 : DP

1, 2, 3 더하기

시간 제한 메모리 제한
1초 512 MB

 

문제

정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 7가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다.

  • 1+1+1+1
  • 1+1+2
  • 1+2+1
  • 2+1+1
  • 2+2
  • 1+3
  • 3+1

정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 11보다 작다.

 

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

 

예제 입력

3
4
7
10

 

예제 출력

7
44
274​

풀이

T(n)을 정수 n을 1, 2, 3의 합으로 나타내는 방법이라고 하자.

점화식 : T(n) = T(n-1) + T(n-2) + T(n-3)

 

출처

- 백준 9095번 : https://www.acmicpc.net/problem/9095

알고리즘 : DFS

import java.io.*;
import java.util.*;

public class Main {
	private int computers;
	private int numOfPair;
	private final int MAX = 105;
	private boolean[][] conn = new boolean[MAX][MAX];
	private boolean[] visited = new boolean[MAX];
	private int totalCnt = 0;
	public static void main(String[] args) throws Exception {
		Main main = new Main();
		main.start();	
	}
	
	private void start() throws Exception {
		System.setIn(new FileInputStream("src/웜바이러스/input.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		computers = Integer.parseInt(br.readLine());
		numOfPair = Integer.parseInt(br.readLine());
		StringTokenizer st;
		for(int i =0 ;i<numOfPair; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			int p1 = Integer.parseInt(st.nextToken());
			int p2 = Integer.parseInt(st.nextToken());
			conn[p1][p2] = true;
			conn[p2][p1] = true;
		}
		dfs(1);
		System.out.println(totalCnt);
		br.close();
	}
	private void dfs(int start) {
		for(int i = 2; i <= computers; i++) {
			if(conn[start][i] && !visited[i]) {
				visited[i] = true;
				totalCnt++;
				dfs(i);
			}
		}
	}
}

출처

- https://www.acmicpc.net/problem/2606

+ Recent posts