백준 9694번무엇을 아느냐가 아니라 누구를 아느냐가 문제다 [C++]

2024. 5. 11. 23:24· [백준]/C++
목차
  1.  
  2. #개요
  3. #첫번째 코드 - 다익스트라/dfs가지치기
  4. #두번째 코드 - 다익스트라 /경로기억
  5. #세번째 코드 - dfs 가지치기
  6. #결론
반응형

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

 

 

#개요

 

다익스트라를 활용한 문제라는걸 쉽게 알아 볼 수 있는데

 

관건은 다익스트라로 알게된 최소비용 

    + 경로를 기억하는게 관건이다.

 

이 경로를 기억하는데서 좀 삽질을 했다.

 

#첫번째 코드 - 다익스트라/dfs가지치기

 

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <algorithm>

using namespace std;

vector<vector<pair<int, int>>> nodes;
vector<int> dist;
vector<bool> visited;
vector<int> path;
vector<int> resultSeq;
int N, M;
int intimacy;
int minIntimacy;

void dijkstra(int start) {
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push({ 0, start });
    dist[start] = 0;

    while (!pq.empty()) {
        int curDist = pq.top().first;
        int curNode = pq.top().second;
        pq.pop();

        if (dist[curNode] < curDist) continue;

        for (auto edge : nodes[curNode]) {
            int nextNode = edge.first;
            int weight = edge.second;
            int sumWeight = curDist + weight;

            if (sumWeight < dist[nextNode]) {
                dist[nextNode] = sumWeight;
                pq.push({ sumWeight, nextNode });
            }
        }
    }
}

void dfs(int node) {
    if (node == M - 1) {
        if (intimacy < minIntimacy) {
            minIntimacy = intimacy;
            resultSeq = path; 
        }
        return;
    }

    visited[node] = true;
    for (auto a : nodes[node]) {
        int nextNode = a.first;
        int weight = a.second;
        if (!visited[nextNode] && intimacy + weight < minIntimacy) {  
            path.push_back(nextNode);
            intimacy += weight;
            dfs(nextNode);
            path.pop_back();
            intimacy -= weight;
        }
    }
    visited[node] = false;
}

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

    int T;
    cin >> T;
    for (int t = 1; t <= T; t++) {
        cin >> N >> M;

        nodes.assign(M, vector<pair<int, int>>());
        dist.assign(M, INT_MAX);
        visited.assign(M, false);
        path.clear();
        resultSeq.clear();

        for (int i = 0; i < N; i++) {
            int x, y, z;
            cin >> x >> y >> z;
            nodes[x].push_back({ y, z });
            nodes[y].push_back({ x, z });
        }

        dijkstra(0); // Dijkstra로 각 노드까지의 최소 친밀도 계산

        intimacy = 0;
        minIntimacy = INT_MAX;
        path.push_back(0);
        visited[0] = true;  // 시작 노드 방문 표시

        dfs(0); // DFS로 최소 경로 탐색

        cout << "Case #" << t << ": ";
        if (!resultSeq.empty()) {
            for (int i : resultSeq) {
                cout << i << ' ';
            }
            cout << '\n';
        }
        else {
            cout << -1 << '\n';
        }
    }

    return 0;
}

 

우선 이 첫번째 코드는

다익스트라로 경로의 최소비용들을 알아낸 뒤에

void dijkstra(int start) {
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push({ 0, start });
    dist[start] = 0;

    while (!pq.empty()) {
        int curDist = pq.top().first;
        int curNode = pq.top().second;
        pq.pop();

        if (dist[curNode] < curDist) continue;

        for (auto edge : nodes[curNode]) {
            int nextNode = edge.first;
            int weight = edge.second;
            int sumWeight = curDist + weight;

            if (sumWeight < dist[nextNode]) {
                dist[nextNode] = sumWeight;
                pq.push({ sumWeight, nextNode });
            }
        }
    }
}

 

dfs 탐색을 이용해서 최소비용 경로를 찾아냈다.

 

void dfs(int node) {
    if (node == M - 1) {
        if (intimacy < minIntimacy) {
            minIntimacy = intimacy;
            resultSeq = path; 
        }
        return;
    }

    visited[node] = true;
    for (auto a : nodes[node]) {
        int nextNode = a.first;
        int weight = a.second;
        if (!visited[nextNode] && intimacy + weight < minIntimacy) {  
            path.push_back(nextNode);
            intimacy += weight;
            dfs(nextNode);
            path.pop_back();
            intimacy -= weight;
        }
    }
    visited[node] = false;
}

 

 

다만 여기서 잘 살펴보면 dfs 는 완전탐색시 

최대 M이 20이기 때문에 20! 이라서

시간초과가 난다 

때문에 가지치기를 사용해서

시간복잡도를 줄여줘야 정답을 맞을 수 있다.

 

if (!visited[nextNode] && intimacy + weight < minIntimacy) {  
            path.push_back(nextNode);
            intimacy += weight;
            dfs(nextNode);
            path.pop_back();
            intimacy -= weight;
        }

가지치기조건이 달려있는걸 확인 할 수 있다.

 

 

하지만 이렇게 복잡하게 풀 이유가 없다

다익스트라를 할때 경로만 기억해주면 되기 때문이다.

 

#두번째 코드 - 다익스트라 /경로기억

 

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <algorithm>

using namespace std;

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

    int T;
    cin >> T;
    for (int t = 1; t <= T; t++) {
        int N, M;
        cin >> N >> M;
        vector<vector<pair<int, int>>> adj(M);
        for (int i = 0; i < N; i++) {
            int x, y, z;
            cin >> x >> y >> z;
            adj[x].push_back({y, z});
            adj[y].push_back({x, z});
        }

        // 다익스트라 알고리즘
        vector<int> dist(M, INT_MAX);
        vector<int> prev(M, -1);
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
        pq.push({0, 0}); // 시작점: 한신이
        dist[0] = 0;
        while (!pq.empty()) {
            int u = pq.top().second;
            int d = pq.top().first;
            pq.pop();
            if (d > dist[u]) continue;
            for (auto& edge : adj[u]) {
                int v = edge.first;
                int weight = edge.second;
                if (dist[v] > dist[u] + weight) {
                    dist[v] = dist[u] + weight;
                    prev[v] = u;
                    pq.push({dist[v], v});
                }
            }
        }

        // 결과 출력
        cout << "Case #" << t << ": ";
        if (dist[M-1] == INT_MAX) {
            cout << "-1\n";
        } else {
            vector<int> path;
            for (int cur = M-1; cur != -1; cur = prev[cur]) {
                path.push_back(cur);
            }
            reverse(path.begin(), path.end());
            for (int node : path) {
                cout << node << " ";
            }
            cout << "\n";
        }
    }
    return 0;
}

 

훨씬 코드가 짧아지고 신뢰성이 올라갔다

 

물론 dfs 만 사용해서 가지치기만 하더라도 답을 얻을 수 있다.

 

#세번째 코드 - dfs 가지치기

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>

using namespace std;

vector<vector<pair<int, int>>> adj;
vector<bool> visited;
vector<int> path;
vector<int> bestPath;
int N, M;
int bestCost = INT_MAX;

void dfs(int node, int cost) {
    if (cost >= bestCost) return;  // 가지치기: 이미 최소 비용보다 크면 중단
    if (node == M - 1) {  // 최고의원에 도달한 경우
        bestCost = cost;
        bestPath = path;
        return;
    }

    visited[node] = true;
    for (auto& edge : adj[node]) {
        int next = edge.first;
        int weight = edge.second;
        if (!visited[next]) {
            path.push_back(next);
            dfs(next, cost + weight);
            path.pop_back();
        }
    }
    visited[node] = false;
}

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

    int T;
    cin >> T;
    for (int t = 1; t <= T; t++) {
        cin >> N >> M;
        adj.assign(M, vector<pair<int, int>>());
        visited.assign(M, false);
        for (int i = 0; i < N; i++) {
            int x, y, z;
            cin >> x >> y >> z;
            adj[x].push_back({y, z});
            adj[y].push_back({x, z});
        }

        bestCost = INT_MAX;
        path.clear();
        bestPath.clear();
        path.push_back(0);

        dfs(0, 0);

        cout << "Case #" << t << ": ";
        if (bestCost == INT_MAX) {
            cout << "-1\n";
        } else {
            for (int i : bestPath) {
                cout << i << " ";
            }
            cout << "\n";
        }
    }
    return 0;
}

 

 

#결론

 

dfs를 이용한 가지치기는

시간복잡도를 확실하기 계산하기 어렵기 때문에

다익스트라로 경로만 기억하는 풀이가 제일 좋다고 할 수 있다.

반응형

'[백준] > C++' 카테고리의 다른 글

백준 27498번 연애 혁명 [C++]  (0) 2024.06.06
백준 18352번 특정 거리의 도시 찾기 [C++]  (0) 2024.05.12
백준 20056번 마법사 상어와 파이어볼 [C++]/삼성 SW역량 테스트 기출 문제  (0) 2024.05.11
백준 14719번 빗물 [C++]  (0) 2024.05.09
백준 21610번 마법사 상어와 비바라기 [C++]/삼성 SW역량 테스트 기출 문제  (0) 2024.05.08
  1.  
  2. #개요
  3. #첫번째 코드 - 다익스트라/dfs가지치기
  4. #두번째 코드 - 다익스트라 /경로기억
  5. #세번째 코드 - dfs 가지치기
  6. #결론
'[백준]/C++' 카테고리의 다른 글
  • 백준 27498번 연애 혁명 [C++]
  • 백준 18352번 특정 거리의 도시 찾기 [C++]
  • 백준 20056번 마법사 상어와 파이어볼 [C++]/삼성 SW역량 테스트 기출 문제
  • 백준 14719번 빗물 [C++]
경우42
경우42
개발 등 공부기록용 블로그입니다
경우42
경우없는 개발 블로그
경우42
전체
오늘
어제
  • 분류 전체보기 (225)
    • 후기 (1)
    • [Codeforces] (4)
    • [SW Expert Academy] (10)
    • [백준] (149)
      • C++ (144)
      • C# (4)
      • python (1)
    • [프로그래머스] (15)
      • lv.3 (8)
      • lv.2 (4)
      • lv.1 (3)
    • [CS(Computer Science)] (2)
      • 자료구조 (2)
    • 알고리즘 (32)
      • Tip (6)
      • 코드 (15)
      • SQL 문법 정리 (10)
    • 웹개발지식 (2)
    • 스프링 (2)
    • 딥러닝 (0)
    • [가톨릭대주변음식점] (2)
      • 런칭&모니터링 (0)
      • 개발 (0)
      • 트러블 슈팅 (2)
    • [만냠-밥약속매칭플랫폼] (1)
    • [일정정리 웹 개발] (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 10989번
  • 백준
  • 프로그래머스
  • 2751번
  • 플로이드-워셜
  • 2003번
  • 4920번
  • lv.2
  • c#
  • 두 포인터
  • 5585번
  • 14246번
  • 냠톨릭
  • 9012번
  • C++
  • 10845번
  • 코드 #다익스트라
  • 11365번
  • 17352번
  • 133300번

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
경우42
백준 9694번무엇을 아느냐가 아니라 누구를 아느냐가 문제다 [C++]
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.