코딩테스트/백준

[백준][C++]1948번 임계경로

윤깡패 2023. 9. 22. 13:35

문제

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

 

1948번: 임계경로

첫째 줄에 도시의 개수 n(1 ≤ n ≤ 10,000)이 주어지고 둘째 줄에는 도로의 개수 m(1 ≤ m ≤ 100,000)이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 도로의 정보가 주어진다. 처음에는 도로의

www.acmicpc.net

 

 

풀이

1. 정방향 위상 정렬 수행

  • 시작점 : 출발 도시
  • 각 도시와 관련된 임계 경로 저장

 

2. 역방향 위상 정렬 수행

  • 시작점 : 도착 도시
  • 현재 임계 경로값 + 해당 도로값 == 이전 도시의 임계 경로값 ⇒ 1분도 쉬지 않고 달려야 하는 도로로 저장

 

위상 정렬(Topology Sort) 알고리즘

 

위상 정렬(Topology Sort) 알고리즘

위상 정렬(Topology Sort) 알고리즘 사이클이 없는 방향 그래프에서 모든 노드를 방향성에 거스르지 않도록 순서대로 나열하는 알고리즘 특징 - 항상 유일한 값으로 정렬되지 않는다. - 사이클이 없

soobin0821.tistory.com

 

 

코드

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  int n, m;	// 도시 수, 도로 수
  cin >> n >> m;

  vector<vector<pair<int, int>>> outcity(n + 1);	// 도시 인접 리스트
  vector<vector<pair<int, int>>> incity(n + 1);	// 역방향 도시 인접 리스트
  vector<int> indegree(n + 1, 0);	// 진입 차수 배열
  vector<int> time(n + 1, 0);
  for(int i = 0; i < m; i++)
  {
    int s, e, c;
    cin >> s >> e >> c;

    outcity[s].push_back(make_pair(e, c));
    incity[e].push_back(make_pair(s, c));	// 역방향 에지 정보 저장
    indegree[e]++;	// 진입 차수 배열 초기화
  }

  int start, end;
  cin >> start >> end;
  
  queue<int> myqueue;	// 큐 생성하기
  // 위상 정렬 수행
  myqueue.push(start);	// 출발 도시를 큐에 삽입
  while(!myqueue.empty())
  {
    int now_city = myqueue.front();
    myqueue.pop();

    for(pair<int, int> next : outcity[now_city])
    {
      int next_city = next.first;
      int cost = next.second;

      time[next_city] = max(time[next_city], time[now_city] + cost);
      if(--indegree[next_city] == 0)
      {
        myqueue.push(next_city);	// 큐에 타깃 노드 추가
      }
    }
  }

  cout << time[end] << "\n";	// 만나는 시간 출력
  
  // 위상 정렬 reverse
  vector<bool> visited(n + 1, false);	// 각 도시의 방문 여부 저장
  visited[end] = true;
  int count = 0;	// 1분도 쉬지 않고 달려야 하는 도로의 수
  myqueue.push(end);	// 도착 도시를 큐에 삽입
  while(!myqueue.empty())
  {
    int now_city = myqueue.front();
    myqueue.pop();
    
    for(pair<int, int> before : incity[now_city])
    {
      int before_city = before.first;
      int cost = before.second;
      // 1분도 쉬지 않는 도로 체크
      if(time[now_city] == time[before_city] + cost)
      {
        count++;	// 1분도 쉬지 않고 달려야 하는 도로값 1 증가
        // 중복 카운트 방지를 위해 이미 방문한 노드 제외
        if(visited[before_city] == false)
        {
          visited[before_city] = true;	// visited 배열에 방문 도시 표시
          myqueue.push(before_city);	// 큐에 타깃 노드 추가
        }
      }
    }
  }

  cout << count;	// 1분도 쉬지 않고 달려야 하는 도로의 수 출력

  return 0;
}

 

 

오답

  • 정방향 위상 정렬 수행 시, 타깃 노드의 진입 차수가 0일 경우 큐에 타깃 노드 추가
  • 역방향 위상 정렬 수행 시, 아직 방문하지 않은 도시이면 큐에 타깃 노드 추가

 

더보기
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int main() 
{
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  int N, M;
  cin >> N >> M;

  vector<vector<pair<int, int>>> A;
  vector<vector<pair<int, int>>> reverseA;
  vector<int> indegree;  // 진입 차수 배열
  A.resize(N + 1);
  reverseA.resize(N + 1);
  indegree.resize(N + 1);
  
  for(int i = 0; i < M; i++)
  {
    int S, E, V;
    cin >> S >> E >> V;
    A[S].push_back(make_pair(E, V));
    reverseA[E].push_back(make_pair(S, V));  // 역방향 에지 정보 저장
    indegree[E]++;  // 진입 차수 배열 초기화
  }

  int startDosi, endDosi;
  cin >> startDosi >> endDosi;
  queue<int> mqueue;
  // 위상 정렬 수행
  mqueue.push(startDosi);
  vector<int> result;
  result.resize(N + 1);

  while(!mqueue.empty())
  {
    int now = mqueue.front();
    mqueue.pop();

    for(pair<int, int> next : A[now])
    {
      indegree[next.first]--;
      result[next.first] = max(result[next.first], result[now] + next.second);
      if(indegree[next.first] == 0)
      {
        mqueue.push(next.first);
      }
    }
  }
  // 위상 정렬 reverse
  int resultCount = 0;
  vector<int> visited;
  visited.resize(N + 1);
  queue<int> rqueue;
  rqueue.push(endDosi);
  visited[endDosi] = true;

  while(!rqueue.empty())
  {
    int now = rqueue.front();
    rqueue.pop();

    for(pair<int, int> next : reverseA[now])
    {
      // 1분도 쉬지 않는 도로 체크
      if(result[next.first] + next.second == result[now])
      {
        resultCount++;
        // 중복 카운트 방지를 위해 이미 방문한 노드 제외
        if(visited[next.first] == false)
        {
          visited[next.first] = true;
          rqueue.push(next.first);
        }
      }
    }
  }
  cout << result[endDosi] << "\n";
  cout << resultCount << "\n";
}

 

 

 

 

 

728x90