[백준] 1939. 중량제한

2025. 8. 28. 18:54·코딩테스트/문제풀이

https://www.acmicpc.net/problem/1939

 

 

🎯 문제 정리

  1. 섬 N개가 다리로 연결되어 있고, 다리마다 최대 무게 제한이 있다. (2 ≤ N ≤10^4)
  2. 섬A → 섬B로 짐을 옮길 때, 한 번에 옮길 수 있는 최대 무게를 구해야 한다.
  3. 여러 경로가 있으면 전체 경로 무게는 그 경로에서 가장 작은 다리 무게 제한으로 결정된다. ⭐️
    이 때의 무게 제한을 구한다.

 

예시를 기반으로 흐름 정리

주어진 조건에 따르면, 1번 섬에 있는 공장에서 2번 섬에 있는 공장으로 가는 경로 중 최대로 짐을 실을 수 있는 경로를 찾고, 그 경로에서 요구하는 무게 제한을 반환해야 한다.

그러면 여기서 우선 순위를 생각해봤을 때, 어떤 섬A → 섬B 경로가 여러 개가 있을 경우 우선시되는 쪽은 경로 중 가장 많이 짐을 실을 수 있는 경로가 된다. 다시 말해 무게 제한이 큰 경로가 우선된다.

 

주어진 예를 기반해서 생각해보면:

1 → 2: 제한 2
1 → 3: 제한 3

1에서 바로 2로 가면 2톤, 3으로 가면 3톤이므로 1 → 3 경로가 우선된다.

1 → 2: 제한 2
3 → 2: 제한 5

하지만 1 → 3으로 갈 때 3톤까지만 가능했으므로 3 → 2의 5톤 다리는 의미가 없고 실제 제한은 3톤으로 결정된다.

1 → 2: 제한 2
1 → 3 → 2: 제한 3

즉 최종 비교는 위와 같다. 따라서 최적 경로는 1 → 3 → 2 (제한 3톤)이 된다.

 

구조화

🎯 `dp[i] = i번 섬까지 도달할 수 있는 최대 무게`
     처음에는 모든 섬의 값을 0으로 초기화하고, 출발 섬만 무한대로 설정한다.
  • 모든 섬은 0으로 초기화:
    시작할 때는 아직 어떤 섬에도 건너가지 않은 상태이므로 기본값을 `짐을 안 옮긴 상태(0)`로 둔다.
    이후 경로를 따라가면서 단계적으로 값을 갱신한다.
  • 출발 섬은 무한대(∞)로 초기화:
    출발하는 공장에서는 처음에 옮길 수 있는 짐의 양에 제한이 없다고 볼 수 있다. 하지만 실제로는 곧 다리를 건너야 하므로 그 순간 다리의 무게 제한에 맞춰 짐이 줄어든다.
    🤔 출발하는 공장에 엄청 많은 재고가 쌓여 있고, 다른 섬으로 옮기기 위해 다리를 건너게 되면서 그 다리가 버틸 수 있는 무게에 따라 짐을 덜어낸다고 생각했다.

섬의 최대 무게 업데이트 규칙

현재 섬 A에서 연결된 섬 B로 이동하려고 할 때:

  1. 새로운 무게 제한은 `dp[A]와 다리(A-B)의 무게 제한 중 작은 값`이 된다.
    어떤 경로에서 옮길 수 있는 무게는 결국 경로를 구성하는 모든 다리의 제한 중 가장 작은 무게로 결정되기 때문에, 섬 A까지 옮겨올 수 있었던 무게(dp[A])와 새로 건너야 하는 다리의 무게 제한 중 더 작은 값으로 결정된다.
  2. 만약 이 `새로운 무게 제한이 dp[B]보다 크다면`, 지금까지 알고 있던 B로 가는 최대 무게보다 더 많은 짐을 옮길 수 있는 새로운 경로를 찾은 것이므로 dp[B]를 갱신하고 방문 큐에 추가한다.

 

구현(1): 시간 초과 발생

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

/*
  N개 섬 (2~10^4) - 다리로 연결 (양방향, 같은 두 섬 사이 여러 다리 가능)
  섬 2개에 공장 
  공장 -> 공장 수송 -- 다리 중량 제한
  >>> 한 번 이동할 때 옮길 수 있는 중량 최댓값 
*/
public class Main {
  public static void main(String[] args) throws IOException{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    
    StringTokenizer st = new StringTokenizer(br.readLine());
    int N = Integer.parseInt(st.nextToken()); //섬
    int M = Integer.parseInt(st.nextToken()); //다리 
    
    //시작 섬:{연결 다리}
    Map<Integer, List<Bridge>> map = new HashMap<>();
    
    for(int i=0; i<M; i++) {
      st = new StringTokenizer(br.readLine());
      int node1 = Integer.parseInt(st.nextToken()); //섬1
      int node2 = Integer.parseInt(st.nextToken()); //섬2
      int weight = Integer.parseInt(st.nextToken()); //중량 제한
      
      //양방향
      map.computeIfAbsent(node1, k -> new ArrayList<>()).add(new Bridge(node2, weight));
      map.computeIfAbsent(node2, k -> new ArrayList<>()).add(new Bridge(node1, weight));
    }
    
    st = new StringTokenizer(br.readLine());
    int fac1 = Integer.parseInt(st.nextToken()); //공장1 
    int fac2 = Integer.parseInt(st.nextToken()); //공장2
    
    //공장1 -> 공장2로 가는 경로에서 최소 무게 제한 
    System.out.println(dj(N, map, fac1, fac2));
  }
  
  static int dj(int N, Map<Integer, List<Bridge>> map, int fac1, int fac2) {
    int[] dp = new int[N+1]; //0으로 초기화
    dp[fac1] = 1000000000; //출발지
    
    PriorityQueue<Bridge> pq = new PriorityQueue<>();  //더 큰 무게 우선 큐
    pq.offer(new Bridge(fac1, dp[fac1]));
    
    while(!pq.isEmpty()) {
      Bridge curr = pq.poll();
      
      if(map.get(curr.to) == null) continue;
      
      for(Bridge next : map.get(curr.to)) {
        //새로운 무게 제한: 현재 섬의 무게 제한과 현재 연결 다리의 무게 제한 중 더 작은 값 
        int newLimit = Math.min(curr.weight, next.weight);
        
        //새로운 무게 제한이 저장된 값(섬의 무게 제한)보다 클 경우에만 업데이트하고 방문 
        if(dp[next.to] < newLimit) {
          dp[next.to] = newLimit; 
          pq.offer(new Bridge(next.to, newLimit));
        }
      }
    }
    
    return dp[fac2];
  }
  
  static class Bridge implements Comparable<Bridge> {
    int to, weight;
    public Bridge (int to, int weight) {
      this.to=to; this.weight=weight;
    }

    //더 큰 무게 제한이 우선 
    @Override
    public int compareTo(Bridge b) {
      return b.weight - this.weight;
    }
  }
}

 

생각한 흐름대로 구현하니까 시간 초과가 발생했다. 

구현(2): 시간 초과 해결

`boolean[] visited`를 활용하여 섬 방문 여부를 기록한다.
어차피 각 섬에 도달할 때마다 더 많은 짐을 실을 수 있는 경로만 업데이트되므로 최초로 방문하는 순간 해당 섬까지의 최대 무게가 확정된다. 따라서 이미 방문한 섬은 더 이상 탐색하지 않도록 막아 불필요한 계산을 줄인다.
import java.util.*;
import java.io.*;

/*
  N개 섬 (2~10^4) - 다리로 연결 (양방향, 같은 두 섬 사이 여러 다리 가능)
  섬 2개에 공장 
  공장 -> 공장 수송 -- 다리 중량 제한
  >>> 한 번 이동할 때 옮길 수 있는 중량 최댓값 
*/
public class Main {
  public static void main(String[] args) throws IOException{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    
    StringTokenizer st = new StringTokenizer(br.readLine());
    int N = Integer.parseInt(st.nextToken()); //섬
    int M = Integer.parseInt(st.nextToken()); //다리 
    
    //시작 섬:{연결 다리}
    Map<Integer, List<Bridge>> map = new HashMap<>();
    
    for(int i=0; i<M; i++) {
      st = new StringTokenizer(br.readLine());
      int node1 = Integer.parseInt(st.nextToken()); //섬1
      int node2 = Integer.parseInt(st.nextToken()); //섬2
      int weight = Integer.parseInt(st.nextToken()); //중량 제한
      
      //양방향
      map.computeIfAbsent(node1, k -> new ArrayList<>()).add(new Bridge(node2, weight));
      map.computeIfAbsent(node2, k -> new ArrayList<>()).add(new Bridge(node1, weight));
    }
    
    st = new StringTokenizer(br.readLine());
    int fac1 = Integer.parseInt(st.nextToken()); //공장1 
    int fac2 = Integer.parseInt(st.nextToken()); //공장2
    
    //공장1 -> 공장2로 가는 경로에서 최소 무게 제한 
    System.out.println(dj(N, map, fac1, fac2));
  }
  
  static int dj(int N, Map<Integer, List<Bridge>> map, int fac1, int fac2) {
    int[] dp = new int[N+1]; //0으로 초기화
    dp[fac1] = 1000000000; //출발지
    
    boolean[] visited = new boolean[N+1];
    
    PriorityQueue<Bridge> pq = new PriorityQueue<>();  //더 큰 무게 우선 큐
    pq.offer(new Bridge(fac1, dp[fac1]));
    
    while(!pq.isEmpty()) {
      Bridge curr = pq.poll();
      
      //이미 방문했던 적 있는 섬이면 무시 
      if(visited[curr.to]) continue;
      
      //방문한 적 없으면 방문 처리 
      visited[curr.to] = true;
      
      //공장2에 도착했으면 종료 
      if(curr.to == fac2) return dp[fac2]; 
      
      
      if(map.get(curr.to) == null) continue;
      for(Bridge next : map.get(curr.to)) {
        //새로운 무게 제한: 현재 섬의 무게 제한과 현재 연결 다리의 무게 제한 중 더 작은 값 
        int newLimit = Math.min(curr.weight, next.weight);
        
        //새로운 무게 제한이 저장된 값(섬의 무게 제한)보다 클 경우에만 업데이트하고 방문 
        if(dp[next.to] < newLimit) {
          dp[next.to] = newLimit; 
          pq.offer(new Bridge(next.to, newLimit));
        }
      }
    }
    
    return dp[fac2];
  }
  
  static class Bridge implements Comparable<Bridge> {
    int to, weight;
    public Bridge (int to, int weight) {
      this.to=to; this.weight=weight;
    }

    //더 큰 무게 제한이 우선 
    @Override
    public int compareTo(Bridge b) {
      return b.weight - this.weight;
    }
  }
}

'코딩테스트 > 문제풀이' 카테고리의 다른 글

[백준] 1854. K번째 최단 거리  (0) 2025.09.13
[백준] 9084. 동전  (1) 2025.09.11
[백준] 5719. 거의 최단 경로  (1) 2025.08.30
[리트코드] 790. Domino and Tromino Tiling  (1) 2025.08.24
[리트코드] 399. Evaluate Division  (7) 2025.08.14
'코딩테스트/문제풀이' 카테고리의 다른 글
  • [백준] 9084. 동전
  • [백준] 5719. 거의 최단 경로
  • [리트코드] 790. Domino and Tromino Tiling
  • [리트코드] 399. Evaluate Division
마동탁
마동탁
보고또보고
  • 마동탁
    원데이투데이
    마동탁
  • 전체
    오늘
    어제
    • 분류 전체보기 (33) N
      • 개발 (2)
        • Elasticsearch (2)
      • CS (21) N
        • 자료구조 (15)
        • 네트워크 (6) N
      • 코딩테스트 (10)
        • 문제풀이 (8)
        • 알고리즘 (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
마동탁
[백준] 1939. 중량제한
상단으로

티스토리툴바