백준 16681번 (등산, C++, Dijkstra) [BAEKJOON]

등산

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

시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초256 MB318082756822.576%

문제

주환이는 요즘 등산에 빠졌다. 주환이는 등산을 위해 지도를 가지고 있는데,

그 지도에는 각 지점의 높이와 갈 수 있는 다른 지점까지의 거리가 표시되어 있다.

주환이는 아침에 집에서 출발하여 등산을 갔다가, 오후 수업을 듣기 위해 고려대학교로 돌아와야 한다.

1. 주환이는 지도의 임의의 지점을 골라, 그 지점을 목표로 정한다. 집 또는 고려대학교는 목표로 선택할 수 없다.

2. 주환이가 집에서 정한 목표에 도달할 때 까지는 항상 높이가 증가하는 방향으로만 이동해야 한다.

3. 주환이가 정한 목표에 도달한 후, 고려대학교로 갈 때에는 항상 높이가 감소하는 방향으로만 이동해야 한다.

4. 주환이는 거리 1을 움직일 때 마다 의 체력이 소모된다.

5. 주환이는 정한 목표에 도달하면 높이 1당 E 의 성취감을 얻는다. 즉 높이가 h인 목표에 도달하면 hE의 성취감을 얻는다.

주환이는 이 등산의 가치를 (얻은 성취감) – (소모한 체력) 으로 계산하기로 하였다.

주환이를 위해 가치가 가장 높은 등산 경로를 선택해주자.

입력

첫 번째 줄에 지도에 표시된 지점의 개수, 지점을 잇는 경로의 개수, 주환이의 거리 비례 체력 소모량,

높이 비례 성취감 획득량을 나타내는 정수 NMDE가 공백을 사이에 두고 주어진다.

(2 ≤ N ≤ 100,000, 1 ≤ M ≤ 200,000, 1 ≤ D ≤ 100, 1 ≤  ≤ 100)

두 번째 줄에 N개의 정수 h1, …  ,hN이 공백으로 구분되어 주어진다. hi는 번째 지점의 높이를 의미한다.

(1 ≤ hi ≤ 1,000,000, 1 ≤ ≤ N)

세 번째 줄부터 M개의 줄에 걸쳐 세 정수 a, b, n이 공백으로 구분되어 주어진다.

이는 a번 지점과 b번 지점을 잇는 거리 n의 양방향 경로가 있음을 의미한다. (1 ≤ a, b ≤ N, 1 ≤ n ≤ 100,000)

어떤 지점에서 다른 지점으로 가는 경로가 여러 개 있을 수도 있으며 (등산로는 여러 개가 있을 수 있다),

한 지점에서 출발해 그 지점으로 돌아가는 경로가 있을 수도 있다 (쉼터에서 몇 바퀴 돌며 쉴 수도 있다).

주환이의 집은 1번 지점에 위치하고, 고려대학교는 N번 지점에 위치하며 주환이의 집과 고려대학교의 높이는 1임이 보장된다.

출력

첫 번째 줄에 주환이가 얻을 수 있는 가치의 최댓값을 출력한다.

만약 조건을 만족하는 등산 경로를 선택할 수 없다면, “Impossible“을 쌍따옴표를 제외하고 출력한다.

답이 음수일 수 있음에 유의하여라.

예제 입력 1

8 13 4 9
1 4 7 3 10 2 15 1
1 2 3
3 4 2
5 6 6
7 8 2
2 3 4
6 7 2
3 6 1
4 8 3
5 1 6
8 3 5
2 5 4
4 6 3
5 3 8

예제 출력 1

15

예제 입력 2

3 2 1 1
1 1 1
1 2 5
2 3 5

예제 출력 2

Impossible

출처

University > 고려대학교 > 2018 고려대학교 프로그래밍 경시대회 (KCPC) > Advanced Div. C번

University > 고려대학교 > 2018 고려대학교 프로그래밍 경시대회 (KCPC) > Intermediate Div. E번

알고리즘 분류


통과된 코드

자료형 long long 필수!

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

constexpr int MAXN = 100001;
constexpr long long int INF = INT64_MAX;
constexpr long long int uINF = INT64_MIN;

///*
//각 노드에 연결되어 있는 노드에 대한 정보를 담는 벡터
//a번 노드에서 b번 노드로 가는 비용이 c라는 의미
//graph[a].push_back((make_pair(B, C));
//*/
vector<pair<int, int>> upGraph[MAXN];
vector<pair<int, int>> downGraph[MAXN];

// Dijkstra 알고리즘에 사용할 우선순위 큐
priority_queue<pair<long long int, int>> myPQ;

// N 지점의 개수, M 지점을 잇는 경로의 개수
// D 주환이의 거리 비례 체력 소모량, E 높이 비례 성취감 획득량
int N, M, D, E;

// 각 지점의 높이를 저장하는 배열
int hArr[MAXN];

// 등산을 하는데 필요한 배열 (집 -> 산)
long long int upArr[MAXN];

long long int temp = 0;
// 결과를 저장
long long int result = uINF;

// 하산을 하는데 필요한 배열 (산 -> 고려대)
long long int downArr[MAXN];

// V1,2 지점, d : 지점 사이의 거리
int V1, V2, d;

void Dijkstra(int start, long long int arr[], vector<pair<int, int>> graph[])
{
    // 임시배열 초기화
    for (int i = 1; i <= N; i++) arr[i] = INF;
    // 우선순위 큐에 삽입.
    myPQ.push({ 0, start }); // < first : 거리 , second : 노드 인덱스 >
    arr[start] = 0;
    while (!myPQ.empty()) {
        // -를 붙이는 이유는 우선순위 큐를 이용하여 정렬하기 위함이다.
        // (최소힙으로 구현)
        long long int nCost = -myPQ.top().first;
        int now = myPQ.top().second;
        myPQ.pop();
        // 이미 담겨 있는 것보다 거리가 크면 넘어간다.
        if (arr[now] < nCost) continue;
        // 해당 노드에서 연결된 모든 경로를 확인
        for (int i = 0; i < graph[now].size(); i++) {
            // disSum = 임시 노드 + 현재 노드에서 i로가는 비용
            long long int disSum = nCost + graph[now][i].second;
            // 비용이 더 작다면 최단경로 테이블 값을 갱신.
            if (disSum < arr[graph[now][i].first]) {
                // 임시 노드 업데이트
                arr[graph[now][i].first] = disSum;
                // 우선순위 큐에 (거리, 노드 인덱스) 푸시
                myPQ.push(make_pair(-disSum, graph[now][i].first));
            }
        }
    }
}


int main()
{
    ios_base::sync_with_stdio(false); // scanf와 동기화를 비활성화
    // cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다.
    cin.tie(NULL);
    cout.tie(NULL);

    // 문제의 조건들을 입력 받는다.
    cin >> N >> M >> D >> E;

    // 각 지점의 높이를 입력 받는다.
    for (int i = 1; i <= N; i++) cin >> hArr[i];

    // 경로를 입력 받는다.
    for (int i = 0; i < M; i++) {

        cin >> V1 >> V2 >> d;

        // 높이가 같을 경우에는 생각하지 않는다.
        if (hArr[V1] > hArr[V2]) { // V1의 높이가 더 높을 경우
            upGraph[V2].push_back(make_pair(V1, d));
            downGraph[V2].push_back(make_pair(V1, d));
        }
        else if (hArr[V1] < hArr[V2]) { // V1의 높이가 더 낮은 경우
            upGraph[V1].push_back(make_pair(V2, d));
            downGraph[V1].push_back(make_pair(V2, d));
        }

    }

    Dijkstra(1, upArr, upGraph);
    Dijkstra(N, downArr, downGraph);

    for (int i = 1; i <= N; i++) {
        if (upArr[i] == INF || downArr[i] == INF) continue;
        temp = upArr[i] + downArr[i];
        result = max(result, hArr[i] * E - temp * D);
    }

    if (result == uINF) cout << "Impossible";
    else cout << result;
    
    return 0;
}

최악의 경우 예를 들어 N = 100,000 / M = 200,000 / hi = i / 모든 경로 거리 1 / D = 1 / E = 100 이런 식이면

int 를 사용하면 터진다. ㅠㅠ

long long int 로 바꾸어주자

로직 문제로 오인해서 많이 틀렸다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤