ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 2211(네트워크 복구)
    전공/알고리즘 2020. 6. 25. 13:55

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

     

    2211번: 네트워크 복구

    첫째 줄에 두 정수 N, M이 주어진다. 다음 M개의 줄에는 회선의 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 컴퓨터와 B번 컴퓨터가 통신 시간이 C (1 ≤ C ≤ 10)인 회선으로 연결되어 있다�

    www.acmicpc.net

    모든 연결이 끊겨 있는 상태에서 어떤 조건들을 만족하면서 최소의 연결방법을 구하는 문제이다. 

     

    어떤 조건을 빼고 보면 최소 스패닝 트리라고 보면 된다. 

     

    1부터 시작해서 인접한 곳에서 방문하지 않았던 지점 중 거리가 최소인 지점을 연결한다.  

    이 결과는 총 연결 거리의 최소를 만들어 내는 알고리즘이다. 

     

    하지만 문제에서 

     

    1. 네트워크를 복구해서 통신이 가능하도록 만드는 것도 중요하지만, 해커에게 공격을 받았을 때 보안 패킷을 전송하는 데 걸리는 시간도 중요한 문제가 된다. 따라서 슈퍼컴퓨터가 다른 컴퓨터들과 통신하는데 걸리는 최소 시간이, 원래의 네트워크에서 통신하는데 걸리는 최소 시간보다 커져서는 안 된다.

    이렇게 조건이 주어져 있기 때문에 2->3까지의 연결 고리가 단순 최소라고 바로 이어서는 안된다. 

     

    1->3 vs 1->2 + 2->3 을 비교해서 1->3 보다 크면 2->3을 연결하면 안된다.  

     

     dist[p2.second.second] < dist[p2.second.first] + p2.first

    위 경우라면 문제 조건에 맞지 않으므로 패스

     

    그래서 여기에 다익스트라 알고리즘이 추가된다. 

     

    플로이드 와샬(모든 정점에서 다른 모든 정점까지 가는 모든 최단거리를 구하는 알고리즘) 을 생각했었지만 

    보안 패킷은 1에서 시작하므로 그렇게 구할 필요도 없고, O(N^3) => 10^9이므로 시간초과가 난다. 

     

    그래서 한 지점에서 다른 모든 지점까지 가는 다익스트라 알고리즘을 사용했다. 

     

     

    먼저 1을 기준으로 최소 지점의 거리와 index를 구한다. 

    그 다음 index를 지나면서 1->index + index->특정 지점 vs 1->특정지점을 비교해서 만약 1->특정지점보다 작으면 dist[]배열을 최신화 해준다. 이렇게 모든 지점을 통해서 다른 지점을 확인하고 나면, 1에서 다른 지점으로 가는 최단 거리 배열을 구할 수 있다. 

     

    void di_init() {
    	for (int i = 2; i <= N; i++) {
    		dist[i] = conn[1][i];
    		pq.push(make_pair(-dist[i], i));
    	}
    	while (!pq.empty()) {
    		p = pq.top();
    		pq.pop();
    		if (!check[p.second]) {
    			check[p.second] = true;
    			for (int i = 2; i <= N; i++) {
    				if (check[i] || p.second == i) continue;
    				if (dist[i] > dist[p.second] + conn[p.second][i]) {
    					dist[i] = dist[p.second] + conn[p.second][i];
    					pq.push(make_pair(-dist[i], i));
    				}
    			}
    		}
    	}
    }
    

     

    그런 다음 dist배열을 토대로 문제 조건 1.에서 말한 조건을 비교하면서 스패닝트리를 만든다. 

     

    최소 스패닝 트리 + 문제 조건

     

    최소 스패닝 트리와 다익스트라는 모드 priority_queue를 사용했다. 

     

    #include<iostream>
    #include<queue>
    #include<utility>
    using namespace std;
    #define MAX 1001
    
    priority_queue<pair<int, int> > pq;
    priority_queue<pair<int, pair<int, int> > > sol_pq;
    queue<pair<int, int> > answer;
    pair<int, int> p;
    pair<int, pair<int, int> > p2;
    int N, M, a, b, c, INF = 987654321;
    int visit_num = 0;
    int conn[MAX][MAX] = { 0 };
    int dist[MAX];
    bool visited[MAX] = { false };
    bool check[MAX] = { false };
    
    
    void di_init() {
    	for (int i = 2; i <= N; i++) {
    		dist[i] = conn[1][i];
    		pq.push(make_pair(-dist[i], i));
    	}
    	while (!pq.empty()) {
    		p = pq.top();
    		pq.pop();
    		if (!check[p.second]) {
    			check[p.second] = true;
    			for (int i = 2; i <= N; i++) {
    				if (check[i] || p.second == i) continue;
    				if (dist[i] > dist[p.second] + conn[p.second][i]) {
    					dist[i] = dist[p.second] + conn[p.second][i];
    					pq.push(make_pair(-dist[i], i));
    				}
    			}
    		}
    	}
    
    }
    
    void solve() {
    	for (int i = 2; i <= N; i++) {
    		sol_pq.push(make_pair(-conn[1][i], make_pair(1, i)));
    	}
    	while (!sol_pq.empty()) {
    		p2 = sol_pq.top();
    		sol_pq.pop();
    		p2.first = -p2.first;
    		if (visited[p2.second.second] || dist[p2.second.second] < dist[p2.second.first] + p2.first) continue;
    		visited[p2.second.second] = true;
    		visit_num++;
    		answer.push(make_pair(p2.second.first, p2.second.second));
    		for (int i = 2; i <= N; i++) {
    			if (visited[i]) continue;
    			sol_pq.push(make_pair(-conn[p2.second.second][i], make_pair(p2.second.second, i)));
    		}
    	}
    
    }
    
    int main() {
    	cin >> N >> M;
    
    	for (int i = 0; i < M; i++) {
    		cin >> a >> b >> c;
    		conn[a][b] = c;
    		conn[b][a] = c;
    	}
    
    	for (int i = 1; i <= N; i++) {
    		for (int j = 1; j <= N; j++) {
    			if (i == j) continue;
    			if (conn[i][j] == 0) conn[i][j] = INF;
    		}
    	}
    
    	di_init();
    	solve();
    	cout << visit_num << "\n";
    	while (!answer.empty()) {
    		p = answer.front();
    		answer.pop();
    		cout << p.first << " " << p.second << "\n";
    	}
    
    }
    

     

    '전공 > 알고리즘' 카테고리의 다른 글

    백준 1504(특정한 최단 경로)  (0) 2020.06.26
    백준 1238(파티)  (0) 2020.06.26
    백준 1644(소수의 연속합)  (0) 2020.06.25
    백준 2206(벽 부수고 이동하기)  (0) 2020.06.25
    백준 9019(DSLR)  (0) 2020.06.22

    댓글

Designed by Tistory.