백준 2211번 (네트워크 복구, C++, Dijkstra) / 추가 반례 [BAEKJOON]

네트워크 복구

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

시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초192 MB56442849196048.951%

문제

N(1 ≤ N ≤ 1,000)개의 컴퓨터로 구성된 네트워크가 있다.

이들 중 몇 개의 컴퓨터들은 서로 네트워크 연결이 되어 있어 서로 다른 두 컴퓨터 간 통신이 가능하도록 되어 있다.

통신을 할 때에는 서로 직접 연결되어 있는 회선을 이용할 수도 있으며, 회선과 다른 컴퓨터를 거쳐서 통신을 할 수도 있다.

각 컴퓨터들과 회선은 그 성능이 차이가 날 수 있다.

따라서 각각의 직접 연결되어 있는 회선을 이용해서 통신을 하는데 걸리는 시간이 서로 다를 수 있다.

심지어는 직접 연결되어 있는 회선이 오히려 더 느려서, 다른 컴퓨터를 통해서 통신을 하는 것이 더 유리할 수도 있다.

직접 연결되어 있는 회선을 사용할 경우에는 그 회선을 이용해서 통신을 하는 데 드는 시간만큼이 들게 된다.

여러 개의 회선을 거치는 경우에는 각 회선을 이용해서 통신을 하는 데 드는 시간의 합만큼의 시간이 걸리게 된다.

어느 날, 해커가 네트워크에 침입하였다.

네트워크의 관리자는 우선 모든 회선과 컴퓨터를 차단한 후, 해커의 공격을 막을 수 있었다.

관리자는 컴퓨터에 보안 시스템을 설치하려 하였는데, 버전 문제로 보안 시스템을 한 대의 슈퍼컴퓨터에만 설치할 수 있었다.

한 컴퓨터가 공격을 받게 되면, 네트워크를 통해 슈퍼컴퓨터에 이 사실이 전달이 되고,

그러면 슈퍼컴퓨터에서는 네트워크를 이용해서 보안 패킷을 전송하는 방식을 사용하기로 하였다.

준비를 마친 뒤, 관리자는 다시 네트워크를 복구하기로 하였다. 이때, 다음의 조건들이 만족되어야 한다.

1. 해커가 다시 공격을 할 우려가 있기 때문에, 최소 개수의 회선만을 복구해야 한다.

물론, 그렇다면 아무 회선도 복구하지 않으면 되겠지만, 이럴 경우 네트워크의 사용에 지장이 생기게 된다.

따라서 네트워크를 복구한 후에 서로 다른 두 컴퓨터 간에 통신이 가능하도록 복구해야 한다.

2. 네트워크를 복구해서 통신이 가능하도록 만드는 것도 중요하지만,

해커에게 공격을 받았을 때 보안 패킷을 전송하는 데 걸리는 시간도 중요한 문제가 된다.

따라서 슈퍼컴퓨터가 다른 컴퓨터들과 통신하는데 걸리는 최소 시간이,

원래의 네트워크에서 통신하는데 걸리는 최소 시간보다 커져서는 안 된다.

원래의 네트워크에 대한 정보가 주어졌을 때,

위의 조건을 만족하면서 네트워크를 복구하는 방법을 알아내는 프로그램을 작성하시오.

입력

첫째 줄에 두 정수 N, M이 주어진다.

다음 M개의 줄에는 회선의 정보를 나타내는 세 정수 A, B, C가 주어진다.

이는 A번 컴퓨터와 B번 컴퓨터가 통신 시간이 C (1 ≤ C ≤ 10)인 회선으로 연결되어 있다는 의미이다.

컴퓨터들의 번호는 1부터 N까지의 정수이며, 1번 컴퓨터는 보안 시스템을 설치할 슈퍼컴퓨터이다.

모든 통신은 완전쌍방향 방식으로 이루어지기 때문에,

한 회선으로 연결된 두 컴퓨터는 어느 방향으로도 통신할 수 있다.

출력

첫째 줄에 복구할 회선의 개수 K를 출력한다.

다음 K개의 줄에는 복구한 회선을 나타내는 두 정수 A, B를 출력한다.

이는 A번 컴퓨터와 B번 컴퓨터를 연결하던 회선을 복구한다는 의미이다.

출력은 임의의 순서대로 하며, 답이 여러 개 존재하는 경우에는 아무 것이나 하나만 출력하면 된다.

예제 입력 1

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
4 5
1 2 1
1 4 4
1 3 2
4 2 2
4 3 3
4 5 1 2 1 1 4 4 1 3 2 4 2 2 4 3 3
4 5
1 2 1
1 4 4
1 3 2
4 2 2
4 3 3

예제 출력 1

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
3
1 2
3 1
4 2
3 1 2 3 1 4 2
3
1 2
3 1
4 2

https://www.acmicpc.net/board/view/70830 <- 참조

예제 입력 A

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
6 12
5 6 4
4 3 3
6 5 8
5 1 4
1 6 3
6 2 6
1 2 4
3 2 8
4 2 5
5 3 5
1 4 2
2 4 8
6 12 5 6 4 4 3 3 6 5 8 5 1 4 1 6 3 6 2 6 1 2 4 3 2 8 4 2 5 5 3 5 1 4 2 2 4 8
6 12
5 6 4 
4 3 3 
6 5 8 
5 1 4 
1 6 3 
6 2 6 
1 2 4 
3 2 8 
4 2 5 
5 3 5 
1 4 2 
2 4 8

예제 출력 A

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
5
2 1
3 4
4 1
5 1
6 1
5 2 1 3 4 4 1 5 1 6 1
5
2 1
3 4
4 1
5 1
6 1

예제 입력 B

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
8 14
2 4 1
7 6 7
1 5 7
1 4 6
5 3 9
4 1 5
7 8 3
6 1 4
4 2 3
5 6 3
7 5 9
7 1 9
2 1 1
7 4 6
8 14 2 4 1 7 6 7 1 5 7 1 4 6 5 3 9 4 1 5 7 8 3 6 1 4 4 2 3 5 6 3 7 5 9 7 1 9 2 1 1 7 4 6
8 14
2 4 1 
7 6 7 
1 5 7 
1 4 6 
5 3 9 
4 1 5 
7 8 3 
6 1 4 
4 2 3 
5 6 3 
7 5 9 
7 1 9 
2 1 1 
7 4 6

예제 출력 B

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
7
2 1
3 5
4 2
5 1
6 1
7 4
8 7
7 2 1 3 5 4 2 5 1 6 1 7 4 8 7
7
2 1
3 5
4 2
5 1
6 1
7 4
8 7

예제 입력 C

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
24 45
20 16 5
24 7 9
24 18 6
4 2 7
23 5 7
8 19 5
1 6 3
9 8 7
2 3 5
11 12 4
4 20 1
2 16 5
22 11 1
17 10 4
3 7 4
18 10 9
21 2 5
20 18 3
5 10 5
15 2 4
14 10 7
4 15 9
20 10 8
14 12 8
12 8 1
23 14 8
15 24 9
12 5 1
15 8 3
21 15 7
12 7 6
18 12 5
13 7 2
7 10 3
24 5 1
15 10 7
3 2 2
22 5 6
3 16 4
1 12 1
9 17 3
17 12 7
8 12 8
11 15 7
4 9 5
24 45 20 16 5 24 7 9 24 18 6 4 2 7 23 5 7 8 19 5 1 6 3 9 8 7 2 3 5 11 12 4 4 20 1 2 16 5 22 11 1 17 10 4 3 7 4 18 10 9 21 2 5 20 18 3 5 10 5 15 2 4 14 10 7 4 15 9 20 10 8 14 12 8 12 8 1 23 14 8 15 24 9 12 5 1 15 8 3 21 15 7 12 7 6 18 12 5 13 7 2 7 10 3 24 5 1 15 10 7 3 2 2 22 5 6 3 16 4 1 12 1 9 17 3 17 12 7 8 12 8 11 15 7 4 9 5
24 45
20 16 5 
24 7 9 
24 18 6 
4 2 7 
23 5 7 
8 19 5 
1 6 3 
9 8 7 
2 3 5 
11 12 4 
4 20 1 
2 16 5 
22 11 1 
17 10 4 
3 7 4 
18 10 9 
21 2 5 
20 18 3 
5 10 5 
15 2 4 
14 10 7 
4 15 9 
20 10 8 
14 12 8 
12 8 1 
23 14 8 
15 24 9 
12 5 1 
15 8 3 
21 15 7 
12 7 6 
18 12 5 
13 7 2 
7 10 3 
24 5 1 
15 10 7 
3 2 2 
22 5 6 
3 16 4 
1 12 1 
9 17 3 
17 12 7 
8 12 8 
11 15 7 
4 9 5

예제 출력 C

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
23
2 15
3 7
4 20
5 12
6 1
7 12
8 12
9 8
10 5
11 12
12 1
13 7
14 12
15 8
16 20
17 12
18 12
19 8
20 18
21 15
22 11
23 5
24 5
23 2 15 3 7 4 20 5 12 6 1 7 12 8 12 9 8 10 5 11 12 12 1 13 7 14 12 15 8 16 20 17 12 18 12 19 8 20 18 21 15 22 11 23 5 24 5
23
2 15
3 7
4 20
5 12
6 1
7 12
8 12
9 8
10 5
11 12
12 1
13 7
14 12
15 8
16 20
17 12
18 12
19 8
20 18
21 15
22 11
23 5
24 5

출처

  • 빠진 조건을 찾은 사람: his130
  • 어색한 표현을 찾은 사람: mwy3055

알고리즘 분류


풀이 과정

통과된 코드

map을 사용하는 생각이 바로 떠올라서 빠르게 해결

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include <iostream>
#include <vector>
#include <queue>
#include <map>
using namespace std;
// N(1 ≤ N ≤ 1,000)개의 컴퓨터
constexpr int MAXN = 1001;
constexpr int INF = INT32_MAX;
///*
//각 노드에 연결되어 있는 노드에 대한 정보를 담는 벡터
//a번 노드에서 b번 노드로 가는 비용이 c라는 의미
//graph[a].push_back((make_pair(B, C));
//*/
vector<pair<int, int>> graph[MAXN];
// Dijkstra 알고리즘에 사용할 우선순위 큐
priority_queue<pair<int, int>> myPQ;
// 문제의 결과를 출력하기 위한 map
map<int, int> myMap;
// dist[i][j]
// => i 에서 j 까지의 최단거리 (임시 노드)
int timeArr[MAXN];
// dist[i][j]
// => i 에서 j 까지의 최단거리 (임시 노드)
int cntArr[MAXN];
// N : 정점의 개수, M : 간선의 개수
// U: 현재 노드, V : 이웃 노드, t : 통신 시간
int N, M, U, V, t;
void Dijkstra(int start)
{
// 임시배열 초기화
for (int i = 1; i <= N; i++) timeArr[i] = INF;
// 우선순위 큐에 삽입.
myPQ.push({ 0, start }); // < first : 거리 , second : 노드 인덱스 >
timeArr[start] = 0;
while (!myPQ.empty()) {
// -를 붙이는 이유는 우선순위 큐를 이용하여 정렬하기 위함이다.
// (최소힙으로 구현)
int nCost = -myPQ.top().first;
int now = myPQ.top().second;
myPQ.pop();
// 이미 담겨 있는 것보다 거리가 크면 넘어간다.
if (timeArr[now] < nCost) continue;
// 해당 노드에서 연결된 모든 경로를 확인
for (int i = 0; i < graph[now].size(); i++) {
// 0이라면 길이 없다는 의미 continue
if (graph[now][i].second == 0) continue;
// disSum = 임시 노드 + 현재 노드에서 i로가는 비용
int disSum = nCost + graph[now][i].second;
// 비용이 더 작다면 최단경로 테이블 값을 갱신.
if (disSum < timeArr[graph[now][i].first]) {
// 임시 노드 업데이트
timeArr[graph[now][i].first] = disSum;
// 우선순위 큐에 (거리, 노드 인덱스) 푸시
myPQ.push(make_pair(-disSum, graph[now][i].first));
// 만약 map의 key 값이 겹친다면 해당 값을 지운다.
if (myMap.count(graph[now][i].first)) myMap.erase(graph[now][i].first);
// 도착지를 Key 값으로 map에 저장
myMap.insert(make_pair(graph[now][i].first, now));
}
}
}
}
int main()
{
ios_base::sync_with_stdio(false); // scanf와 동기화를 비활성화
// cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다.
cin.tie(NULL);
cout.tie(NULL);
cin >> N >> M;
for (int i = 0; i < M; i++) {
cin >> U >> V >> t;
// 양방향 통신 가능
graph[U].push_back(make_pair(V, t));
graph[V].push_back(make_pair(U, t));
}
Dijkstra(1);
// map의 사이즈를 출력합니다.
cout << myMap.size() << "\n";
// 복구한 회선을 출력
for (auto it = myMap.begin(); it != myMap.end(); it++)
cout << it->first << " " << it->second << "\n";
return 0;
}
#include <iostream> #include <vector> #include <queue> #include <map> using namespace std; // N(1 ≤ N ≤ 1,000)개의 컴퓨터 constexpr int MAXN = 1001; constexpr int INF = INT32_MAX; ///* //각 노드에 연결되어 있는 노드에 대한 정보를 담는 벡터 //a번 노드에서 b번 노드로 가는 비용이 c라는 의미 //graph[a].push_back((make_pair(B, C)); //*/ vector<pair<int, int>> graph[MAXN]; // Dijkstra 알고리즘에 사용할 우선순위 큐 priority_queue<pair<int, int>> myPQ; // 문제의 결과를 출력하기 위한 map map<int, int> myMap; // dist[i][j] // => i 에서 j 까지의 최단거리 (임시 노드) int timeArr[MAXN]; // dist[i][j] // => i 에서 j 까지의 최단거리 (임시 노드) int cntArr[MAXN]; // N : 정점의 개수, M : 간선의 개수 // U: 현재 노드, V : 이웃 노드, t : 통신 시간 int N, M, U, V, t; void Dijkstra(int start) { // 임시배열 초기화 for (int i = 1; i <= N; i++) timeArr[i] = INF; // 우선순위 큐에 삽입. myPQ.push({ 0, start }); // < first : 거리 , second : 노드 인덱스 > timeArr[start] = 0; while (!myPQ.empty()) { // -를 붙이는 이유는 우선순위 큐를 이용하여 정렬하기 위함이다. // (최소힙으로 구현) int nCost = -myPQ.top().first; int now = myPQ.top().second; myPQ.pop(); // 이미 담겨 있는 것보다 거리가 크면 넘어간다. if (timeArr[now] < nCost) continue; // 해당 노드에서 연결된 모든 경로를 확인 for (int i = 0; i < graph[now].size(); i++) { // 0이라면 길이 없다는 의미 continue if (graph[now][i].second == 0) continue; // disSum = 임시 노드 + 현재 노드에서 i로가는 비용 int disSum = nCost + graph[now][i].second; // 비용이 더 작다면 최단경로 테이블 값을 갱신. if (disSum < timeArr[graph[now][i].first]) { // 임시 노드 업데이트 timeArr[graph[now][i].first] = disSum; // 우선순위 큐에 (거리, 노드 인덱스) 푸시 myPQ.push(make_pair(-disSum, graph[now][i].first)); // 만약 map의 key 값이 겹친다면 해당 값을 지운다. if (myMap.count(graph[now][i].first)) myMap.erase(graph[now][i].first); // 도착지를 Key 값으로 map에 저장 myMap.insert(make_pair(graph[now][i].first, now)); } } } } int main() { ios_base::sync_with_stdio(false); // scanf와 동기화를 비활성화 // cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다. cin.tie(NULL); cout.tie(NULL); cin >> N >> M; for (int i = 0; i < M; i++) { cin >> U >> V >> t; // 양방향 통신 가능 graph[U].push_back(make_pair(V, t)); graph[V].push_back(make_pair(U, t)); } Dijkstra(1); // map의 사이즈를 출력합니다. cout << myMap.size() << "\n"; // 복구한 회선을 출력 for (auto it = myMap.begin(); it != myMap.end(); it++) cout << it->first << " " << it->second << "\n"; return 0; }
#include <iostream>
#include <vector>
#include <queue>
#include <map>

using namespace std;

// N(1 ≤ N ≤ 1,000)개의 컴퓨터
constexpr int MAXN = 1001;
constexpr int INF = INT32_MAX;

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

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

// 문제의 결과를 출력하기 위한 map 
map<int, int> myMap;

// dist[i][j]  
// => i 에서 j 까지의 최단거리 (임시 노드)
int timeArr[MAXN];

// dist[i][j]  
// => i 에서 j 까지의 최단거리 (임시 노드)
int cntArr[MAXN];

// N : 정점의 개수, M : 간선의 개수
// U: 현재 노드, V : 이웃 노드, t : 통신 시간
int N, M, U, V, t;


void Dijkstra(int start)
{
    // 임시배열 초기화
    for (int i = 1; i <= N; i++) timeArr[i] = INF;
    // 우선순위 큐에 삽입.
    myPQ.push({ 0, start }); // < first : 거리 , second : 노드 인덱스 >
    timeArr[start] = 0;

    while (!myPQ.empty()) {
        // -를 붙이는 이유는 우선순위 큐를 이용하여 정렬하기 위함이다.
        // (최소힙으로 구현)
        int nCost = -myPQ.top().first;
        int now = myPQ.top().second;
        myPQ.pop();

        // 이미 담겨 있는 것보다 거리가 크면 넘어간다.
        if (timeArr[now] < nCost) continue;

        // 해당 노드에서 연결된 모든 경로를 확인
        for (int i = 0; i < graph[now].size(); i++) {
            // 0이라면 길이 없다는 의미 continue
            if (graph[now][i].second == 0) continue;
            // disSum = 임시 노드 + 현재 노드에서 i로가는 비용
            int disSum = nCost + graph[now][i].second;
            // 비용이 더 작다면 최단경로 테이블 값을 갱신.
            if (disSum < timeArr[graph[now][i].first]) {
                // 임시 노드 업데이트
                timeArr[graph[now][i].first] = disSum;
                // 우선순위 큐에 (거리, 노드 인덱스) 푸시
                myPQ.push(make_pair(-disSum, graph[now][i].first));

                // 만약 map의 key 값이 겹친다면 해당 값을 지운다.
                if (myMap.count(graph[now][i].first)) myMap.erase(graph[now][i].first);
                // 도착지를 Key 값으로 map에 저장
                myMap.insert(make_pair(graph[now][i].first, now));
            }
        }
    }
}

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

    cin >> N >> M;

    for (int i = 0; i < M; i++) {
        cin >> U >> V >> t;
        // 양방향 통신 가능
        graph[U].push_back(make_pair(V, t));
        graph[V].push_back(make_pair(U, t)); 
    }

    Dijkstra(1);

    // map의 사이즈를 출력합니다.
    cout << myMap.size() << "\n";

    // 복구한 회선을 출력
    for (auto it = myMap.begin(); it != myMap.end(); it++) 
        cout << it->first << " " << it->second << "\n";

	return 0;
}

댓글 달기

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