[그래프] [시도 x] PROMISES 선거 공약

2021. 2. 22. 18:19알고리즘/알고리즘 문제 [hard]

1. 문제

 

새로 건설할 당위성이 있다:

 

고속도로를 통해 오갈 수 없던 두 도시가 새로 연결됨.

 

두 도시를 오가는 데 걸리는 시간이 단축 되어야함

 

 

N년간 하나씩 고속도로를 건설할때

이들 중 몇 개가 의미가 없는지 계산하여라

 

2. 플로이드 n번

 

역시 간단한 풀이는 그냥 건설할때마다 플로이드를 수행하는 것이다,

하지만 이는 시간초과로 문제를 시간안에 해결할 수 없다.

 

또다른 방법은 플로이드를 계산할때  a, b 정점을 경유하는지 기록하는 것

만약 고속도로가 추가되면 이 기록을따라 a->b, b->a거리를 업데이트 시키고,

b와 a에 대해서만 플로이드를 다시 수행하는 것이다.

하지만 이것은 구현도 까다롭고 복잡하다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <algorithm>
#include <vector>
#include <iostream>
#include <utility>
 
using namespace std;
 
const int INF = 123456789;
 
int C;
int V;
int M;
int N;
 
vector<vector<int> > adj;
vector<vector<int> > graph;
void floyd()
{
    for(int i = 0; i < V; i++)
        for(int j = 0; j < V; j++
            if(i == j)
                adj[i][i] = 0;
            else
                adj[i][j] = graph[i][j];
                    
            
    for(int k = 0; k < V; k++)
    {
        for(int i = 0; i < V; i++)
        {
            for(int j = 0; j < V; j++)
            {
                adj[i][j] = min(adj[i][j], adj[i][k] + adj[k][j]);
           }
        }
    }
}
 
int add(int a, int b, int c)
{
    if(adj[a][b] > c || adj[b][a] > c)
    {
        graph[a][b] = c;
        graph[b][a] = c;
        floyd();
        return 0;
    }
    return 1;
}
 
int main()
{
    cin>>C;
    while(C--)
    {
        int ret = 0;
        cin>>V>>M>>N;
        adj = vector<vector<int> > (V, vector<int>(V, INF));
        graph = vector<vector<int> > (V, vector<int>(V, INF));
        
        for(int i = 0; i < M; i++)
        {
            int a, b, c;
            cin>>a>>b>>c;
            c = min(c, graph[a][b]);
            graph[a][b] = c;
            graph[b][a] = c;
        }
        floyd();
 
        for(int i = 0 ; i < N; i++)
        {
            int a, b, c;
            cin>>a>>b>>c;
            ret += add(a, b, c);
        }
        cout<<ret<<"\n";
 
    }
}
cs

 

3. 책의 풀이: 최단 거리의 갱신

 

그래프에 대해 플로이드 알고리즘을 한 번 수행해서 최단 거리 행렬 adj를 먼저 얻어낸다.

 

그 후 그래프에 새 간선이 추가되었을 때 adj를 적절히 갱신해야한다.

 

 

 

위에서 a-b의 간선이 주어졌을때 a와 b에서만 플로이드를 적용시키면된다에서 더 나아가

 

책에서는 adj[i][a] + c + adj[b][j] / adj[i][b] + c + adj[a][j] / adj[i][j] 에서 가장 작은것으로 adj[i][j]를 업데이트 시켰다.

 

a 와 b가 끝점이나 시작점인것에 c 를 더한것이 adj[i][j]의 값으로 업데이트된다는 것은 

 

이 경로가 추가된 a-b b-a 로 인해 더 작은 시간을 소모하게된것을 의미하고, 연결되지 않았으면 연결한다는 것이다.

 

 

이것은 플로이드 알고리즘의 동작원리를 이용한것이며

 

경유할 수 있는 정점의 목록을 늘려가는 대신 경유할 수 있는 간선의 목록을 늘려가는것으로 변형시킨것이다.

 

이러한 변형은 유도과정을 생각해보면 알고리즘의 정당성을 해치지 않는다.

 

 

간선의 집합 e를 경유할 수 있을 때 u에서 v로 가는 최단 거리를 De(u, v)라고하자

이때 e의 원소 중 간선 (a, b)가 있다고하고 e-{(a-b)} = e' 라고 쓰면

 

 

De = min{ De'(u, v),

              De'(u, a) + w(a, b) + De'(b, v), 

              De'(u, b) + w(a, b) + De'(a, v)}

 

가 성립한다.

 

간선을 어느 방향으로 타고 갈 것인가를 정해야하기 때문에 경우의 수 가 3개가 됨을 알 수 있다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <algorithm>
#include <vector>
#include <iostream>
#include <utility>
using namespace std;
const int INF = 123456789;
int C;
int V;
int M;
int N;
vector<vector<int> > adj;
vector<vector<int> > graph;
void floyd()
{
    for(int i = 0; i < V; i++)
        for(int j = 0; j < V; j++
            if(i == j)
                adj[i][i] = 0;
            else
                adj[i][j] = graph[i][j];
                    
            
    for(int k = 0; k < V; k++)
    {
        for(int i = 0; i < V; i++)
        {
            for(int j = 0; j < V; j++)
            {
                adj[i][j] = min(adj[i][j], adj[i][k] + adj[k][j]);
            }
        }
    }
}
int add(int a, int b, int c)
{
    if(adj[a][b] > c || adj[b][a] > c)
    {
        for(int i = 0; i < V; i++)
            for(int j = 0; j < V; j++)
                adj[i][j] = min(adj[i][j], min(adj[i][a] + c + adj[b][j], adj[i][b] + c + adj[a][j]));
 
        return 0;
    }
    return 1;
}
int main()
{
    cin>>C;
    while(C--)
    {
        int ret = 0;
        cin>>V>>M>>N;
        adj = vector<vector<int> > (V, vector<int>(V, INF));
        graph = vector<vector<int> > (V, vector<int>(V, INF));
        
        for(int i = 0; i < M; i++)
        {
            int a, b, c;
            cin>>a>>b>>c;
            c = min(c, graph[a][b]);
            graph[a][b] = c;
            graph[b][a] = c;
        }
        floyd();
        for(int i = 0 ; i < N; i++)
        {
            int a, b, c;
            cin>>a>>b>>c;
            ret += add(a, b, c);
        }
        cout<<ret<<"\n";
    }
}
cs

 

 

- 알고리즘 문제해결전략 965p