[문제 링크]



DFS 문제다. 각 방이 노드, 문이 간선인 무방향 그래프로 보고 DFS로 방문하며 출력을 하면 된다.

나는 인접리스트로 문제를 풀었는데,(인접배열로 하기엔 배열의 크기가 너무 크다.)

가장 낮은 번호의 방을 먼저 방문한다고 했으므로, 인접리스트를 정렬 한 뒤에 탐색을 진행하도록 하였다.

밑은 이를 구현한 코드이다.


#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int n,m;
vector<vector<int> > graph;
vector<int> visited;
void dfs(int cur){
    printf("%d ",cur);
    sort(graph[cur-1].begin(),graph[cur-1].end());
    for(int i =0; i<graph[cur-1].size();i++){
        if(visited[graph[cur-1][i]-1]==0){
            visited[graph[cur-1][i]-1] = 1;
            dfs(graph[cur-1][i]);
        }
    }
}
int main(){
    scanf("%d %d",&n,&m);
    graph.resize(n);
    visited.resize(n);
    for(int i = 0; i < m; i++){
        int a,b;
        scanf("%d %d",&a,&b);
        graph[a-1].push_back(b);
        graph[b-1].push_back(a);
    }
    visited[0] = 1;
    dfs(1); 
}


'Algorithm > Problems' 카테고리의 다른 글

알고스팟 - WEEKLYCALENDAR  (0) 2016.05.09
백준 - 10219 Meats On The Grill  (0) 2016.05.09
백준 - 4781 사탕가게  (0) 2016.05.06
백준 - 1238 파티  (0) 2016.04.22
백준 - 1753 최단경로  (1) 2016.04.22

[문제 링크]



DP 문제이다. DP와 그리디 문제 예시로 유명한 '배낭 채우기'문제와 정말 유사한 문제이다.


배낭 채우기 문제와는 다른점은 메모이제이션 할 값이 실수형이라는 점인데, 여기서 소수 둘째자리까지만이라는 제한을 주었으므로,


이는 100을 곱해 자연수로 만든 후 처리가 가능하다.


다만 실수 자료형 부동소수점 문제로 오차가 있을 수 있으니 100을 곱한뒤 소수 첫째 자리에서 반올림해주어야 한다.


그렇게 변형한 금액으로 메모이제이션해 최대 값을 구하면 된다.


밑은 이를 구현한 코드이다.



#include <cstdio>
#include <algorithm>
#include <utility>
#include <cstring>
using namespace std;
int n, c, cache[10001];
double m, p;
typedef pair<int, int> Candy;
Candy cd[5001];
int candyStore(int money) {
    int& ret = cache[money];
    if (ret != -1) return ret;
    ret = 0;
    for (int i = 0; i <n; i++) {
        //남은 금액이 0 이상일 경우에만 비교. 
        if (money - cd[i].second >= 0) {
            int tmp = cd[i].first + candyStore(money - cd[i].second);
            ret = max(ret, tmp);
        }
    }
    return ret;
}
int main() {
    while (true) {
        memset(cache, -1, sizeof(cache));
        scanf("%d %lf", &n, &m);
        if (n == 0 && m == 0) break;
        for (int i = 0; i < n; i++) {
            scanf("%d %lf", &c, &p);
            cd[i] = make_pair(c, (int)(p*100+0.5));
        }
        printf("%d\n", candyStore((int)(m*100+0.5)));
    }
}


'Algorithm > Problems' 카테고리의 다른 글

백준 - 10219 Meats On The Grill  (0) 2016.05.09
더블릿 - 미로찾기/starship_maze  (0) 2016.05.09
백준 - 1238 파티  (0) 2016.04.22
백준 - 1753 최단경로  (1) 2016.04.22
백준 - 9252 LCS 2  (0) 2016.04.17

앞서 다익스트라 알고리즘 포스트에서, 그래프에서 정점끼리의 최단 경로를 구하는 문제는 여러가지 방법이 있다고 했다.


하나의 정점에서 다른 하나의 정점까지의 최단 경로를 구하는 문제(single source and single destination shortest path problem)

하나의 정점에서 다른 모든 정점까지의 최단 경로를 구하는 문제(single source shortest path problem)

하나의 목적지로가는 모든 최단 경로를 구하는 문제(single destination shortest path problem)

마지막으로 모든 최단 경로를 구하는 문제(All pairs shortest path problem).


플로이드-워셜 알고리즘은 이 중에서 마지막, 모든 최단 경로를 구하는 방법이다. (다익스트라는 single source shortest path problem였다.)

또한 다익스트라 알고리즘은 음의 가중치를 가진 간선은 못쓴다는 제약이 있었다. 하지만 플로이드-워셜에선 사용이 가능하다.



모든 정점에 대한 경로를 계산하므로 거리를 저장할 자료구조는 2차원 배열이 된다.

플로이드-워셜은 optimal substructure의 개념을 이용하여 최단 경로를 찾는다. optimal substructure란 특정 경로 안에 무수히 많은 경로가 있을 때, 중간 정점들이 각각 최단이 된다면 이를 모두 이은 경로 또한 최단이 된다는 개념이다.





1. 플로이드-워셜 알고리즘 기본 로직


플로이드-워셜에선 2개의 테이블을 사용하는데. 하나는 모든 경로에 대한 비용을 저장하는 테이블, 나머지 하나는 각 정점까지 가기 직전의 정점을 저장한 테이블이다. 각각의 테이블을 D와 P라고 하겠다.

테이블 D와 P에는 처음엔 인접 리스트에 대한 내용만 들어가게 된다. 그 후 경로를 추가 할 때마다 두 테이블이 갱신된다.

위 그래프를 보자. 플로이드-워셜에선 처음엔 인접리스트의 정보만 들어간다고 했다. 따라서 맨 처음에 초기화되는 테이블 D,P는 다음과 같다.


거리를 저장한 테이블 D

 

1

2

3

4

5

1

0

3

8

INF

-4

2

INF

0

INF

1

7

3

INF

4

0

INF

INF

4

2

INF

-5

0

INF

5

INF

INF

INF

6

0


직전 정점을 저장한 테이블 P

 

1

2

3

4

5

1

NIL

1

1

NIL

1

2

NIL

NIL

NIL

2

2

3

NIL

3

NIL

NIL

NIL

4

4

NIL

4

NIL

NIL

5

NIL

NIL

NIL

5

NIL



여기서 INF는 무한대를 뜻하며, NIL은 직전 정점이 없다(즉 연결되어있지 않다)는 뜻이다. 위 정점은 아무런 정점을 중간 경로로 사용하지 않고, 오직 인접 리스트만 표현한 것이기 때문에, 그 외는 INF,NIL로 명시되었다.


이제 여기서 경로 1을 중간 경로로 하는 테이블을 구해보자. 얼핏 봐도 4->2가 중간경로 1을 추가하면 4->1->2로 접근 할 수 있고, 4->5도 4->1->5로 접근 할 수 있게 됨을 볼 수 있다.


중간 경로 1을 추가한 테이블 D

 

1

2

3

4

5

1

0

3

8

INF

-4

2

INF

0

INF

1

7

3

INF

4

0

INF

INF

4

2

5

-5

0

-2

5

INF

INF

INF

6

0


중간 경로 1을 추가한 테이블 P

 

1

2

3

4

5

1

NIL

1

1

NIL

1

2

NIL

NIL

NIL

2

2

3

NIL

3

NIL

NIL

NIL

4

4

1

4

NIL

1

5

NIL

NIL

NIL

5

NIL


이렇게 경로가 갱신되며, 갱신 될 시 테이블 P의 해당 컬럼 또한 1로 갱신된다. 직전 경로가 갱신되므로 테이블 P를 이용하면 최단 경로 또한 구할 수 있다. 

이를 모든 정점을 경로에 추가할 때 까지 반복한다.


따라서 다음과 같은 관계가 성립됨을 알 수 있다.







2. C++ 플로이드-워셜 알고리즘  코드


밑은 위 식과 개념으로 작성한 코드이다. 여기선 테이블을 두개로 안두고, pair로 그냥 하나의 테이블로 묶었다, 또한 테이블 P와 스택을 이용해서 경로 또한 구했다.

#include <iostream>
#include <vector>
#include <utility>
#include <stack>
#define MAX 987654321
#define NIL -1
using namespace std;
int n, m, x;
//플로이드 와샬 테이블. pair 하나는 D, 하나는 Pi 
vector<vector<pair<int, int> > > fw;
stack<int> path;
int main() {
    scanf("%d %d %d", &n, &m, &x);
    //플로이드 와샬 테이블 초기화. 
    fw.resize(n + 1);
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            fw[i].push_back(make_pair(MAX, NIL));
            if (i == j) fw[i][j].first = 0;
        }
    }
    for (int i = 0; i<m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        fw[u - 1][v - 1].first = w;
        fw[u - 1][v - 1].second = u - 1;
    }

    printf("///////////초기테이블///////////\n");
    printf("--거리\n");
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            printf(fw[i][j].first == MAX ? "INF\t" : "%d\t", fw[i][j].first);
        }
        printf("\n");
    }
    printf("--직전 프로시져\n");
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            printf(fw[i][j].second == NIL ? "NIL\t" : "%d\t", fw[i][j].second+1);
        }
        printf("\n");
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j<n; j++) {
            for (int k = 0; k<n; k++) {
                if (fw[k][j].first > fw[k][i].first + fw[i][j].first) {
                    fw[k][j].first = fw[k][i].first + fw[i][j].first;;
                    fw[k][j].second = fw[i][j].second;;
                }
            }
        }
    }

    printf("\n/////////완료 후 테이블/////////\n");
    printf("--거리\n");
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            printf(fw[i][j].first == MAX ? "INF\t" : "%d\t", fw[i][j].first);
        }
        printf("\n");
    }
    printf("--직전 프로시져\n");
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            printf(fw[i][j].second == NIL ? "NIL\t" : "%d\t", fw[i][j].second+1);
        }
        printf("\n");
    }
    printf("\n");
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            if(fw[i][j].second == NIL) continue;
            printf("%d부터 %d까지 경로 : ",i+1,j+1);
            int pre = j;
            path.push(j+1);
            while (i != fw[i][pre].second) {
                pre = fw[i][pre].second;
                path.push(pre+1);
            }
            printf("%d",i+1);
            while(!path.empty()){
                printf("-%d",path.top()); path.pop();
            }
            printf("\n");
        }
    }

}

입출력 코드가 많아서 그렇지 플로이드-워셜 코드 자체는 되게 짧고 간결하다.




<실행 화면>


[문제 링크]


각 도시에서 한 도시로의 왕복 최단 거리 중에 최장 거리를 구하는 문제이다(?)


directed graph이기 때문에 X라는 도시에 갈 때와, X라는 도시에서 돌아 올 때의 경로는 둘 다 다르다.


난 이 문제를 3가지 방법으로 풀었다. 

.


1. 플로이드-와샬 알고리즘으로 전체 최단 경로를 구해 푸는 방법.

플로이드-와샬 알고리즘으로는 코드도 정말 간단하고,정답도 잘 나왔지만, 플로이드-와샬 자체가 O(n^3)인 알고리즘인지라 시간초과가 났다. 가지치기를 잘 하면 accept 되는거 같긴 한데, 그래도 다익스트라를 이용하는 것 보다는 느릴거란 생각이 들었다.(또 아무래도 정점의 갯수에 비헤 간선이 워낙 적기 때문이기도 한거같다.)


2. 다익스트라 알고리즘으로 X에서 출발하는 모든 최단 경로를 구한 뒤, 각 정점별로의 최단거리 또한 구해서 X까지 가는 방법.

모든 정점에 대해서 다익스트라 알고리즘을 사용해서 최대값을 구했다. 이건 accept가 뜨긴 떴다. 하지만 개인적으로 만족스럽지 않았다. 실행 속도도 속도지만, 굳이 각 정점에 대해서 최단 거리를 구할 필요가 있나 생각이 들었다. 


3. 다익스트라 알고리즘으로 X에서 출발하는 모든 최단 경로를 구한 뒤, 모든 간선을 뒤집어서 X에서 출발하는 정점을 구하는 방법.

일단 정방향으로 되어있는 인접리스트로 최단 경로를 구하고, 그다음 역방향으로 되어있는 인접리스트로 최단 경로를 구했다. 

그냥 간선만 방향을 바꿔주면 다른 모든 정점에서 X로 오는 정점을 구하는 것과 다를 게 없다. 따라서 2번과는 다르게 다익스트라 알고리즘을 단 2번만 사용해서 최대값을 구할 수 있다.




1. 플로이드-와샬 알고리즘으로 구현한 코드

#include <cstdio>
#include <vector>
#include <algorithm>
#include <utility>
#define MAX 987654321
using namespace std;
int n, m, x;
vector<vector<int> > fw;
int main() {
    scanf("%d %d %d", &n, &m, &x);
    fw.resize(n + 1);
    for (int i = 0; i<n; i++) {
        for (int j = 0; j<n; j++) {
            fw[i].push_back(MAX);
            if (i == j) fw[i][j] = 0;
        }
    }
    for (int i = 0; i<m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        fw[u - 1][v - 1] = w;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j<n; j++) {
            for (int k = 0; k<n; k++) {
                if (fw[k][j] > fw[k][i] + fw[i][j])
                    fw[k][j] = fw[k][i] + fw[i][j];
            }
        }
    }
    int ret = 0;
    for (int i = 0; i<n; i++)
        ret = max(ret, fw[i][x] + fw[x][i]);
    printf("%d", ret);
}



2. 다익스트라 알고리즘 N번으로 구현한 코드

#include <cstdio>
#include <queue>
#include <vector>
#include <utility>
#include <algorithm>
#define MAX 987654321
using namespace std;
vector<vector<pair<int, int> > > adj;
int n, m, x;
typedef pair<int, int> node;
struct cmp {
    bool operator()(node x, node y) {
        return x.second > y.second;
    }
};
int ret[1001] = {0,};
void dijkstra(int x,int y) {
    priority_queue<node, vector<node>, cmp> pq;
    vector<int> dist(n+1,MAX); dist[x] = 0;
    pq.push(make_pair(x,0));
    for(int i=0; i<n;i++){
        if(i!=x) pq.push(make_pair(i,MAX));
    }
    while(!pq.empty()){
        int u = pq.top().first;
        int d = pq.top().second; pq.pop();
        if(d > dist[u]) continue;
        for(int i = 0; i<adj[u].size(); i++){
            int v = adj[u][i].first;
            int w = adj[u][i].second;
            if(dist[v] > dist[u] + w){
                dist[v] = dist[u] + w;
                pq.push(make_pair(v,dist[v]));
            }
        }       
    } 
    if(y==x){
        for(int i = 0; i<n; i++) ret[i] = dist[i];
    }
    else{
        ret[x] += dist[y];
    }    
}
int main() {
    scanf("%d %d %d", &n, &m, &x);
    adj.resize(n + 1);
    for (int i = 0; i<m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        adj[u-1].push_back(make_pair(v-1, w));
    }
    dijkstra(x-1,x-1);
    for(int i=0; i<n; i++){
        if(i!=x)
            dijkstra(i,x);
    }
    int mv = 0;
    for(int i=0; i<n;i++) mv = max(mv,ret[i]);   
    printf("%d",mv);
}



3. 다익스트라 알고리즘 2번으로 구현한 코드

#include <cstdio>
#include <queue>
#include <vector>
#include <utility>
#include <algorithm>
#define MAX 987654321
using namespace std;
vector<vector<pair<int, int> > > adj1, adj2;
int n, m, x;
typedef pair<int, int> node;
struct cmp {
    bool operator()(node x, node y) {
        return x.second > y.second;
    }
};
int ret[1001] = {0,};
void dijkstra(int x, vector<vector<pair<int,int> > > adj) {
    priority_queue<node, vector<node>, cmp> pq;
    vector<int> dist(n+1,MAX); dist[x] = 0;
    pq.push(make_pair(x,0));
    for(int i=0; i<n;i++){
        if(i!=x) pq.push(make_pair(i,MAX));
    }
    while(!pq.empty()){
        int u = pq.top().first;
        int d = pq.top().second; pq.pop();
        if(d > dist[u]) continue;
        for(int i = 0; i<adj[u].size(); i++){
            int v = adj[u][i].first;
            int w = adj[u][i].second;
            if(dist[v] > dist[u] + w){
                dist[v] = dist[u] + w;
                pq.push(make_pair(v,dist[v]));
            }
        }       
    } 
    for(int i = 0; i<n; i++) ret[i] += dist[i];  
}
int main() {
    scanf("%d %d %d", &n, &m, &x);
    adj1.resize(n + 1);
    adj2.resize(n + 1);
    for (int i = 0; i<m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        adj1[u-1].push_back(make_pair(v-1, w));
        adj2[v-1].push_back(make_pair(u-1, w));
    }
    dijkstra(x-1,adj1);
    dijkstra(x-1,adj2);
    int mv = 0;
    for(int i=0; i<n;i++) mv = max(mv,ret[i]);   
    printf("%d",mv);
}


'Algorithm > Problems' 카테고리의 다른 글

더블릿 - 미로찾기/starship_maze  (0) 2016.05.09
백준 - 4781 사탕가게  (0) 2016.05.06
백준 - 1753 최단경로  (1) 2016.04.22
백준 - 9252 LCS 2  (0) 2016.04.17
백준 - 9251 LCS  (0) 2016.04.17


[문제 링크]



다익스트라 알고리즘을 사용하면 된다.


다익스트라 알고리즘에 대한 설명은 다음 포스트를 참조하자


http://hsp1116.tistory.com/42



#include <cstdio>
#include <queue>
#include <vector>
#include <utility>
#define MAX 987654321
using namespace std;
vector<vector<pair<int, int> > > adj;
int n, m, x;
typedef pair<int, int> node;
struct cmp {
    bool operator()(node x, node y) {
        return x.second > y.second;
    }
};
void dijkstra(int x) {
    priority_queue<node, vector<node>, cmp> pq;
    vector<int> dist(n+1,MAX); dist[x] = 0;
    pq.push(make_pair(x,0));
    for(int i=1; i<=n;i++){
        if(i!=x) pq.push(make_pair(i,MAX));
    }
    while(!pq.empty()){
        int u = pq.top().first;
        int d = pq.top().second; pq.pop();
        if(d > dist[u]) continue;
        for(int i = 0; i<adj[u].size(); i++){
            int v = adj[u][i].first;
            int w = adj[u][i].second;
            if(dist[v] > dist[u] + w){
                dist[v] = dist[u] + w;
                pq.push(make_pair(v,dist[v]));
            }
        }       
    }
    for(int i = 1; i<=n; i++){
        printf(dist[i] == MAX? "INF\n": "%d\n",dist[i]);
    }
    
}
int main() {
    scanf("%d %d %d", &n, &m, &x);
    adj.resize(n + 1);
    for (int i = 0; i<m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        adj[u].push_back(make_pair(v, w));
    }
    dijkstra(x);
}

'Algorithm > Problems' 카테고리의 다른 글

백준 - 4781 사탕가게  (0) 2016.05.06
백준 - 1238 파티  (0) 2016.04.22
백준 - 9252 LCS 2  (0) 2016.04.17
백준 - 9251 LCS  (0) 2016.04.17
알고스팟 - KOOGLE  (0) 2016.04.14

그래프에서 정점끼리의 최단 경로를 구하는 문제는 여러가지 방법이 있다.


하나의 정점에서 다른 하나의 정점까지의 최단 경로를 구하는 문제(single source and single destination shortest path problem)

하나의 정점에서 다른 모든 정점까지의 최단 경로를 구하는 문제(single source shortest path problem)

하나의 목적지로가는 모든 최단 경로를 구하는 문제(single destination shortest path problem)

마지막으로 모든 최단 경로를 구하는 문제(All pairs shortest path problem)이다.


다익스트라 알고리즘은 여기서 두번째 방법으로, 하나의 정점에서 다른 모든 정점들의 최단 경로를 구한다.

간선들은 모두 양의 간선들을 가져야 한다.


다익스트라 알고리즘의 기본 로직은, 첫 정점을 기준으로 연결되어있는 정점들을 추가해가며, 최단 거리를 갱신하는 것이다.

정점을 잇기 전까지는 시작점을 재외한 정점들은 모두 무한대 값을 가진다.


정점 A에서 정점 B로 이어지면, 정점 B가 가지는 최단 거리는 시작점부터 정점 A까지의 최단 거리 + 점A와 점B 간선의 가중치와, 기존에 가지고 있던 정점 B의 최단 거리중 작은 값이 B의 최단 거리가 된다.



1. 다익스트라 알고리즘 기본 로직




다음과 같은 그래프가 있다. 여기서 시작점은 5번 정점이라고 해보자.

여기서 5번 노드를 제외한 나머지 정점들이 가지는 최단 경로는 아직 연결되지 않았으므로 무한대이다.



 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF



여기서 경로가 가장 짧은 정점을 고른다. 여기선 5번 노드이다. 5번 노드와 연결되어있는 노드는 2,4번 노드이다.

먼저 2번노드부터 보자. 2번 노드의 최단 거리를 가지고있는 현재 최단거리(INF)와, 5번 노드의 최단거리(0) + 2번-5번의 가중치(4) 값 중 가장 작은 값으로 갱신한다. 즉, dist[2] = min(dist[2], dist[5] + adj[5][2])을 구한다 이는 min(INF,4)이므로 4가 된다.

4번 노드 또한 4번 노드의 최단 거리를 가지고있는 현재 최단거리(INF)와, 5번 노드의 최단거리(0) + 4번-5번의 가중치(2) 값 중 가장 작은 값으로 갱신한다.

즉, dist[4] = min(dist[4], dist[5] + adj[5][4])을 구한다. min(INF,2)이므로 2가 된다.


 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF

 

 2

 

 4

 


이제 나머지 정점 중에서 경로가 가장 짧은 정점을 고른다. 가장 짧은 거리를 가지고 있는 노드는 2의 길이를 가지고 있는 4번 노드이다. 

이제 4번 노드의 인접노드는 2,3번 노드이다.

2번 정점의 최단 거리를 가지고있는 현재 최단거리(4)와, 4번 정점의 최단거리(2) + 2번-4번의 가중치(1) 값 중 가장 작은 값으로 갱신한다.

즉, dist[2] = min(dist[2], dist[4] + adj[4][2])을 구한다

2번 정점의 기존 최단 거리 dist[2]는 4고, dist[4] + adj[4][2]는 3이므로 3으로 갱신된다.


또한 3번 정점은 의 최단 경로 dist[3]은 INF이므로, dist[4] + adj[4][3] = 3으로 갱신한다.


 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF

 

 2

 

 4

 

 

 

 3

 3

 


위와 같은 방법을 반복한다. 3,2번 정점의 최단 경로가 똑같으므로 숫자적으로 앞에 있는 2번 경로를 꺼내고, 인접 정점을 비교한다.

인접 정점은 1로 간선 가중치는 3이다. 따라서 dist[1] = min(dist[1], dist[2] + adj[2][1])을 구한다. dist[1]은 INF이므로, dist[2] + adj[2][1]인 6으로 갱신된다.


 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF

 

 2

 

 4

 

 

 

 3

 3

 

 

 

 

 

 6


최단 경로가 3인 정점 3을 꺼내고 인접 정점을 비교한다. 인접 정점은 4다. dist[4] = min(dist[4], dist[3] + adj[3][4])를 계산한다.

dist[4] 는 2이고,dist[3] + adj[3][4]는 5이다. dist[4]가 더 작으므로 그대로 유지된다.



 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF

 

 2

 

 4

 

 

 

 3

 3

 

 

 


 

 6


마지막으로 남은 정점은 1이다. 1을 꺼내고 인접 정점을 비교한다. 인접 정점은 3과 4다. 

dist[4] = min(dist[4], dist[1] + adj[1][4])를 계산한다.

dist[4] 는 2이고,dist[1] + adj[1][4]는 9이다. dist[4]가 더 작으므로 그대로 유지된다.


dist[3] = min(dist[], dist[6] + adj[6][3])를 계산한다.

dist[3] 는 3이고,dist[1] + adj[1][3]는 9이다. dist[4]가 더 작으므로 그대로 유지된다.



 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF

 

 2

 

 4

 

 

 

 3

 3

 

 

 

 

 

 6


모든 정점을 방문했다. 완성한 테이블이 5번 정점부터 나머지 정점까지의 최단 경로이다.




2. 우선순위 큐(힙구조)를 이용한 다익스트라 알고리즘


위 방법은 배열을 매번 탐색해서 가장 짧은 거리를 찾는 방법이다.


하지만 힙 구조를 이용하면 더욱 빠른 시간 내에 구현이 가능하다.


최소 힙을 사용하면 가장 작은 경로를 가진 정점이 나오도록 구현하면 되기 때문이다.


그래프를 다시 한번 보자. 




모든 정점들을 힙(우선순위 큐)에 넣는다. 



dist배열

 5

 4

 3

 2

 1

 0

 INF

 INF

 INF

 INF


위 표에서 i는 정점 인덱스, d는 최단 거리이고 p는 이전 정점이다. d를 기준으로 최소 힙으로 구성한다.

힙에서 가장 위에 있는 값을 꺼낸다. 인덱스가 5이고 최소 거리가 0인 값이다.

기존에 있던 dist[5]와 d를 비교한다. dist[5]가 더 작으면 연산하지 않고, 같거나 크면 연산한다.

둘 다 0으로 같으므로, 5 주변의 인접 정점을 계산하고 기존의 dist[i]보다 더 작아지는 정점들을 큐에 넣는다.(dist[2] = min(dist[2], dist[5] + adj[5][2], dist[4] = min(dist[4], dist[5] + adj[5][4])




dist배열

 5

 4

 3

 2

 1

 0

 2

 INF

 4

 INF


힙에서 가장 위에 있는 값을 꺼낸다. 인덱스가 4이고 최소 거리가 2인 값이다.

기존에 있던 dist[4]와 d를 비교한다. dist[4]가 더 작으면 연산하지 않고, 같거나 크면 연산한다.

dist[4]는 2이고, d는 2로 같으므로,

4 주변의 인접 정점을 계산해서 기존의 dist[i]보다 더 작아지는 정점들을 큐에 넣는다.(dist[2] = min(dist[2], d + adj[4][2], dist[3] = min(dist[3], d + adj[4][3]))




dist배열

 5

 4

 3

 2

 1

 0

 2

 3

 3

 INF


힙에서 가장 위에 있는 값을 꺼낸다. 인덱스가 2이고 최소 거리가 3인 값이다.

기존에 있던 dist[2]와 d를 비교한다. dist[2]가 더 작으면 연산하지 않고, 같거나 크면 연산한다.

dist[2]는 3이고, d는 3으로 같으므로,

2 주변의 인접 정점을 계산해서 기존의 dist[i]보다 더 작아지는 정점들을 큐에 넣는다.(dist[1] = min(dist[1], dist[2] + adj[2][1]))





dist배열

 5

 4

 3

 2

 1

 0

 2

 3

 3

 6


힙에서 가장 위에 있는 값을 꺼낸다. 인덱스가 3이고 최소 거리가 3인 값이다.

기존에 있던 dist[3]와 d를 비교한다. dist[3]가 더 작으면 연산하지 않고, 같거나 크면 연산한다.

dist[3]는 3이고, d는 3으로 같으므로,

2 주변의 인접 정점을 계산해서 기존의 dist[i]보다 더 작아지는정점들을 큐에 넣는다.(dist[4] = min(dist[4], dist[4] + adj[4][2]))

여기서 dist[4]는 2로 계산값보다 더 작으므로 큐에 넣지 않는다.




dist배열

 5

 4

 3

 2

 1

 0

 2

 3

 3

 6


힙에서 가장 위에 있는 값을 꺼낸다. 인덱스가 2이고 최소 거리가 4인 값이다.

dist[2]=3보다 d=4가  더 크므로 계산하지 않는다.




dist배열

 5

 4

 3

 2

 1

 0

 2

 3

 3

 6


힙에서 가장 위에 있는 값을 꺼낸다. 인덱스가 1이고 최소 거리가 7인 값이다.

기존에 있던 dist[1]와 d를 비교한다.dist[1]는 7이고, d는 7로 같으므로,

1 주변의 인접 정점을 계산해서 기존의 dist[i]보다 더 작아지는정점들을 큐에 넣는다.(dist[4] = min(dist[4], dist[1] + adj[1][4]),dist[3] = min(dist[3], dist[1] + adj[1][3]))

둘 다 기존의 dist[i]보다 크므로 큐에 넣지 않는다.




dist배열

 5

 4

 3

 2

 1

 0

 2

 3

 3

 6


큐에 남아있는 정점들은 전부 d가 INF로 dist[i]보다 크므로 계산되지 않는다.



이를 구현한 코드는 밑과 같다. 위에선 작성되어 있지 않지만 갱신 시마다 위의 정보들을 map에 넣어주면 경로를 쉽게 구할 수 있다. 

이 또한 코드에서 구현해보았다.




3. C++ 우선순위 큐 다익스트라 알고리즘 코드

#include <cstdio>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#include <utility>
#define MAX_VALUE -987654321
using namespace std;
int n, m, s;
class Vertex {
public:
    int index;
    int dist;
    int post = 0;
    Vertex(int index, int dist, int post) :index(index), dist(dist), post(post) {
    }
    Vertex(int index) :index(index) {
        dist = MAX_VALUE;
    }
    void setDist(int d) {
        dist = d;
    }
    bool operator >(const Vertex& v) const {
        return dist > v.dist;
    }
    bool operator <(const Vertex& v) const {
        return dist < v.dist;
    }
};
class Graph {
public:
    int n = 0;
    vector<Vertex> vt;
    vector<vector<pair<int, int> > > adj;
    priority_queue<Vertex > pq;
    map<int, pair<int, int> > m;

    void addVertex(int index, int dist) {
        vt.push_back(Vertex(index, dist, index));
        n = vt.size();
        adj.resize(n + 1);
    }
    void addVertex(int index) {
        vt.push_back(Vertex(index));
        n = vt.size();
        adj.resize(n);
    }
    void addAdj(int u, int v, int w) {
        adj[u - 1].push_back(make_pair(v, w));
    }
    void setStart(int index) {
        vt[index - 1].setDist(0);
        vt[index - 1].post = index;
    }
    void dijkstra(int s) {
        vector<int> dist(n, -1 * MAX_VALUE);
        dist[s - 1] = 0;
        setStart(s);
        for (int i = 0; i<n; i++) {
            pq.push(vt[i]);
        }
        m.insert(make_pair(s, make_pair(0, -1)));
        while (!pq.empty()) {
            int index = pq.top().index - 1;
            int cost = -1 * pq.top().dist; pq.pop();
            if (dist[index] < cost) continue;
            for (int i = 0; i<adj[index].size(); i++) {
                int n = adj[index][i].first;
                int v = adj[index][i].second;
                if (dist[n - 1] > dist[index] + v) {
                    dist[n - 1] = dist[index] + v;
                    Vertex newVt = Vertex(n, -dist[n - 1], index + 1);
                    pq.push(newVt);
                    m[n] = make_pair(dist[n - 1], index + 1);

                }
            }
        }

        printf("인덱스 별 최단 경로 길이\n");
        for (int i = 0; i<n; i++) {
            printf(dist[i] != -1 * MAX_VALUE ? "%d " : "INF ", dist[i]);
        }
        printf("\n\n인덱스 - 최단 경로 - 이전 정점\n");
        for (int i = 1; i <= m.size(); i++) {
            printf("%d - %d - %d\n", i, m[i].first, m[i].second);
        }
        printf("\n");
        for (int i = 1; i <= n; i++) {
            printf("%d까지 경로 : ", i);
            if (dist[i - 1] != -MAX_VALUE) {
                stack<int> st;
                int next = m[i].second;
                while (next != -1) {
                    st.push(next);
                    next = m[next].second;
                }
                while (!st.empty()) {
                    printf("%d - ", st.top());
                    st.pop();
                }
                printf("%d\n", i);
            }
        }
    }
};
int main() {
    scanf("%d %d %d", &n, &m, &s);
    Graph gp;
    for (int i = 0; i<n; i++) {
        gp.addVertex(i + 1);
    }
    for (int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        gp.addAdj(u, v, w);
    }
    gp.dijkstra(s);
}


<실행 화면>



편집 거리 알고리즘이란, 두 문자열의 유사도를 판단하는 알고리즘이다.


유사도를 판단하는 기준은, 어떠한 문자열을 삽입,삭제,변경을 몇 번이나 해서 바꿀수 있는지를 계산하여


그 최소값을 구해, 그 값을 유사도 판단의 척도로 다룬다.



간단하게 예를 들어보자. 여기선 LCS와 마찬가지로 2차원 배열을 통해 문자열을 하나 하나씩 비교를 해나간다.


문자열 MICROSOFT와 NCSOFT를 비교한다고 하자. 모든 연속 부분 집합에 대해서 비교를 해야한다.


먼저 두 문자열의 처음 비교 대상은 공집합 두개이다. 둘 다 같은 문자열이기때문에 바꿀 것이 없어 코스트는 0이다.


그 다음은 N과 {}이다 m이 {}이 되려면 하나를 추가 해야한다. 따라서 코스트는 1이다.

이어서 NC와 {}이다. 두개를 추가해야되기 때문에 코스트는 2가된다.

이런 식으로 먼저 테이블을 그리면 다음과 같은 형태를 가진다.


 

 {}

 N

 C

 S

 O

 F

 T

 {}

 0

 1

 2

 3

 4

 5

 6

 M

 1

 

 

 

 

 

 

 I

 2

 

 

 

 

 

 

 C

 3

 

 

 

 

 

 

 R

 4

 

 

 

 

 

 

 O

 5

 

 

 

 

 

 

 S

 6

 

 

 

 

 

 

 O

 7

 

 

 

 

 

 

 F

 8

 

 

 

 

 

 

 T

 9

 

 

 

 

 

 



그 다음 M과 N을 비교해보자. 이 둘은 다르다. 따라서 값을 변경해야 한다. 코스트는 1이다.

이어서 M과 NC이다. 둘 다 다르고, 하나를 지우고 하나를 바꿔야한다.  따라서 코스트는 2이다.

M과 NCS를 비교하자. 셋 다 다르고 두개를 지우고 하나를 바꿔야한다. 따라서 코스트는 3이다.

이런식으로 세 번째 줄 까지만 채워보면 밑과 같다.


 

 {}

 N

 C

 S

 O

 F

 T

 {}

 0

1

2

3

4

5

6

 M

 1

1

2

3

4

5

6

 I

 2

2

2

3

4

5

6

 C

 3

3

2

3

4

5

6

 R

 4

 

 

 

 

 

 

 O

 5

 

 

 

 

 

 

 S

 6

 

 

 

 

 

 

 O

 7

 

 

 

 

 

 

 F

 8

 

 

 

 

 

 

 T

 9

 

 

 

 

 

 



테이블을 직접 그리다보면 다음과 같은 사실을 깨달을 수 있다.

문자열 M과 문자열 NC를 비교한다고 해보자. 여기서 문자열 M과N은 이미 순차적으로 비교를 한 상태이다. 이어서 문자열 M과 C이 만약 같다고 할 때, 이것에 대한 코스트는 바꿔즐 필요가 없다. 따라서 M과 N을 비교했던 값을 가져오기만 하면 된다.

그리고 만약 M과 C가 다르다고 했을 경우엔, M과 N을 비교했던 값에서 코스트를 하나 증가시켜주면 된다.


이 방법을 좀 더 긴 문자열에 적용해보자.

MICROSOFT와 NCSOFT 마지막 T끼리 비교했을 때, T는 서로 일치하므로 그 전까지 비교했던 MICROSOF와 NCSOF의 편집거리를 가져오면 된다.

MICRO와 NCS를 비교 할 경우, O와 S는 서로 다르므로, MICR과 NC를 비교했을때의 편집거리에다가 1을 증가시키면 된다.


여기서 첫 번째, 두 변수가 일치했을 경우 이전의 편집거리를 가져오면 된다고 했다. 

따라서 A[i] == B[j]일때의 편집거리 D(i,j) = D(i-1,j-1)이다.


그리고 두 문자가 다를 경우, 이전의 편집거리에서 1을 증가시켜야 한다고 했다.

MI와 NCS를 비교한다고 해보자. (위 테이블에서 이 경우 비교하는 대상은 I와 S이다. 이를 기억하고 보자.) 

왼쪽 대각선 위에 있는건 M와 NC를 비교하는 편집거리이다. 위는 M과 NCS를 비교하는 편집 거리이며, 왼쪽은 MI와 NC를 비교하는 편집 거리이다.


1. 여기서 대각선 M과 NC의 편집거리를 가져오는 것은 I와 S를 같은 문자로, 두 문자열이 같다고 보는 것이다. 즉 I를 S로 바꾸면 비교하는 문자가 같으니 M과 NC를 비교한 편집거리를 그대로 가져오고, I를 S로 바꾼 수정을 위한 코스트를 더하는 것이다. (위에서 같을 경우엔 대각선위에 있는 값을 그대로 가져왔었던걸 떠올리면 된다.)


2. 위쪽인 M과 NCS의 편집거리에서 1을 증가시켜 MI,NCS 편집거리에 넣는다는 것은 I를 삭제한다는 것을 뜻한다. 즉 M과 NCS의 편집거리를 그대로 가져오고 I를 삭제한 코스트를 추가한다는 뜻이다.


3. 마지막으로 왼쪽인 MI와 NC의 편집거거에서 1을 증가시켜 값을 MI,NCS 편집거리에 넣는다는 것은.  MI 뒤에 문자를 하나 추가한다는 것이다. 즉 MI뒤에 문자열을 하나 추가시켜 MIS,NCS이므로 MI,NC의 편집거리에다 추가시킨 비용을 더한 값을 넣는 것이다.




<그림으로 좀 더 자세히 이해해 보자>



즉 A[i] != B[j]면, 1,2,3번, 각 수정,삽입,삭제를 한 편집거리 중 최소값을 가져오면 된다. 즉 D(i,j) = min(D(i-1,j)+1,D(i,j-1)+1,D(i-1,j-1)+1)이다.



즉 정리하면 다음과 같다.

1. 비교하고자 하는 문자 A[i]와 B[j]가 같으면, D(i,j) = D(i-1,j-1)이다.

2. 비교하고자 하는 문자 A[i]와 B[j]가 다르면, D(i,j) = min(D(i-1,j)+1,D(i,j-1)+1,D(i-1,j-1)+1)이다.


위 규칙 대로 테이블을 채우면 다음과 같다. 규칙대로 한번 채워보고, 직접 문자열을 비교해서 채워서 두 테이블을 비교해보면 확실하게 알 수 있다.



 

 {}

 N

 C

 S

 O

 F

 T

 {}

 0

1

2

3

4

5

6

 M

 1

1

2

3

4

5

6

 I

 2

2

2

3

4

5

6

 C

 3

3

2

3

4

5

6

 R

 4

4

3

3

4

5

6

 O

 5

5

4

4

3

4

5

 S

 6

6

5

4

4

4

5

 O

 7

7

6

5

4

5

5

 F

 8

8

7

6

5

4

5

 T

 9

9

8

7

6

5

4



여기서 맨 마지막 값이 편집거리가 된다.


이를 코드로 옮기면 다음과 같다.



C++ 편집 거리 알고리즘

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;

string input1,input2;
int dist[1001][1001];

int levenshtein(string& input1, string& input2){
    for(int i=1; i<=input1.length(); i++)
        dist[i][0] = i;
    for(int j=1; j<=input2.length(); j++)
        dist[0][j] = j;
        
    for(int j=1; j<=input2.length(); j++){
        for(int i=1; i<=input1.length(); i++){
            if(input1[i-1] == input2[j-1]) dist[i][j] = dist[i-1][j-1];
            else dist[i][j] = min(dist[i-1][j-1]+1,min(dist[i][j-1]+1,dist[i-1][j]+1));
        }
    }   
    for(int j=0; j<=input2.length();j++){
        for(int i=0;i<=input1.length();i++)
            printf("%d\t",dist[i][j]);
            printf("\n");
    }
    return dist[input1.length()][input2.length()];
}

int main(){
    cin>>input1>>input2;
    cout<<"편집 거리 : "<<levenshtein(input1,input2)<<endl;
}

코드를 작성하고 보니 LCS 알고리즘이랑 유사점이 많아보인다는 생각이 들었다.


<실행 화면>






외판원 문제는 NP문제로 유명한 문제이다. 


여러 도시들이 주어져 있고, 모든 도시들에 대한 가중치가 주어졌을때, 단일 시작점부터 시작해서 모든 도시를 단 한번씩만 방문하여 다시 시작점으로 돌아오는데 드는 최단 거리를 구하는 문제이며, 말은 그냥 일반 그래프 문제인거 같아 그리 어려워 보이지 않지만, 무려 NP-hard 문제에 속한다.


이 문제가 어려운게 완전 연결 그래프라는 점인데, 단순히 완전 탐색으로 진행을 하면 무려 O(N!)라는 시간복잡도가 나온다.


이는 10개만 탐색해도 3,628,800이라는 값이 나오며,  20개는 2,432,902,008,176,640,000개라는 상상하기도 힘든 값이 나온다. 

완전 탐색으로도 풀이는 가능하지만 왠만하면 지양해야 하는 방법이다.

DP로도 생각보다 빠른 시간으로 탐색이 안되서, 실제로는 그리디로 '최단 거리'는 못구해도 '그나마 괜찮은 거리'를 구하는 정도로 사용하는 듯 하다.



<C++ 완전 탐색 TSP>

0번 도시부터 시작해서 모든 도시를 순회한 후 최종적으로 다시 0번으로 돌아오는 코드이다.

무식하게 모든 경우의 수를 일일히 다 계산하는 방법이다.

#include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; int n; //가중치를 저장하기 위핸 배열 int dist[15][15]; int TSP(vector<int> path, vector<bool> visited, int len){ //모든 도시 다 방문했을 경우 if(path.size() == n) return len+dist[path.back()][0]; int ret = 987654321; for(int next=0; next<n;next++){ //방문 했다면 패스 if(visited[next]==true) continue; int cur = path.back(); path.push_back(next); visited[next] = true; ret = min(ret,TSP(path,visited,len+dist[cur][next])); visited[next] = false; path.pop_back(); } return ret; } int main(){ cin >> n; for (int i = 0; i < n; i++){ for (int j = 0; j < n; j++){ cin >> dist[i][j]; } } vector<int> path(1, 0); // 경로를 저장할 벡터, 시작 도시 0번도시 선택. vector<bool> visited(n, false); // 방문 여부를 저장할 벡터. false로 초기화. visited[0] = true; // 출발 도시 방문여부 체크. double ret = TSP(path, visited, 0); cout << ret << endl; }


하지만 앞서 말했듯이 이 코드는 15개 이상부터는 어마어마한 시간이 걸린다.


그래서 완전 탐색 말고 DP를 사용하는 방법을 보도록 하자.




<C++ TSP 동적계획법>


동적계획법으로 구현하려면 이를 부분 문제로 쪼개야 한다.

완전 탐색으로 구현했을 때 쓰는 값들은 지금까지의 경로를 저장한 벡터, 방문여부를 저장한 벡터, 지금까지 연결된 거리이다.

동적계획법은 기본적으로 각각 독립적인 부분 문제로 쪼개지므로 지금까지 연결된 거리는 필요가 없고, 현재점을 기준으로 연산을 해야한다.


즉 


라는 점화식으로 풀 수 있다.(여기서 n은 0부터 next까지로, visited를 검사해서 방문한 적이 없는 값으로만 조사를 진행해야된다.)


시작점과, 현재 방문한 도시를 기준으로 모든 도시를 순회해서 얻을 수 있는 최소 거리를 출력하는 식이다.


그럼 거리에 대한 정보를 어떻게 메모이제이션을 할까? 해당 시작점으로 출발하는 경로는 메모이제이션 할때 어떻게 표현할까?


이에 대한 답은 비트마스킹이다. 비트로 도시의 방문 여부를 체크하는 것이다.

예를 들어 0번 도시와 2번 도시를 방문했다면, 00000101 = 3으로 체크가 될 것이고,  0번,3번,5번 도시를 방문했다면 00101001 = 41이 될것이다.

즉 1번 건물에서 1,2,3번 건물을 방문한 것은 cache[1][7]에 메모이제이션 하면 될 것이다.


int형은 4바이트, 총 32비트이기 때문에 int형 변수만으로도 32개 도시를 표현 할 수 있다.


코드를 작성하기전에 비트 연산을 간단하게 보도록 하자.

1<<n : 1을 n만큼 왼쪽으로 시프트한다. 즉 1<<4이면 10000이다.

a = a | (1<<n) : a라는 변수의 n번째 비트를 켠다.

a & (1<<n) : a라는 변수의 n번째 비트가 켜있으면 1<<n을, 꺼있으면 0을 반환한다.

a -=(1<<n) : a라는 변수의 n번째 비트를 끈다(1->0). 단 무조건 켜져 있을 때만 사용해야한다.

a &=~(1<<n) : a라는 변수의 n번째 비트를 끈다. 꺼져 있으면 꺼진 상태로 유지한다.

a ^=(1<<n) : a변수의 n번째 비트를 토글한다. 켜있으면 끄고, 꺼있으면 킨다.


즉 위 비트연산을 이용하면 visited&(1 << next)는 next번째 도시의 방문 여부를 확인하는 것이고.

또한 재귀 호출의 visited인자로 visited | (1 << next)를 전달 하는 것은, 해당 도시의 방문 여부를 체크해서 전달하는 것이다.


밑은 비트마스킹 DP를 이용해 구현한 코드이다.

#include <cstdio> #include <cmath> #include <algorithm> #define MAX_VALUE 987654321.0 using namespace std; int n; //경로를 저장할 dp int cache[17][65536], dist[17][17]; int TSP(int cur, int visited) { //점이 10개라면, 100000000000-1 =011111111111; if (visited == (1 << n)-1) return dist[cur][0]; int& ret = cache[cur][visited]; if (ret != 0) return ret; ret = MAX_VALUE; for (int next = 0; next <= n; next++) { if (visited&(1 << next))continue; if (dist[cur][next]==0) continue; ret = min(ret,TSP(next, visited | (1 << next)) + dist[cur][next]); } return ret; } int main(){ scanf("%d", &n); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ scanf("%d",&dist[i][j]); } } printf("%d",TSP(0,1)); }


완전 탐색으로 푸는 TSP는 O(N!)인것에 반해 DP는 O(2^N*N^2)이다. DP로 풀어도 빠른 속도는 아니지만 완전 탐색에 비해 현저하게 빨라지긴 했다.



<경로 추적하기>


위 소스 코드에서 메모이제이션한 값으로 경로를 추적해보았다.

방법은 간단하다. 이 소스코드는 0이라는 위치부터 시작하므로,

 전체 거리 - 0부터 다른 경로까지의 거리 = cache[k][masking + (1 << k)](k에서 masking에 켜져있는 비트의 도시들과 k번째 도시를 방문한 값)을 만족하는 것을 찾으면, k가 바로 다음 경로가 되는 것이다.

이 k를 경로를 저장하기 위한 벡터에 저장하자.  그 후k를 다음 경로를 찾기 위해 비교하기 위한 변수 piv에 넣는다.

그리고 비교할 거리를 masking에 켜진 도시들과 k를 방문했으며, k부터 시작하는 거리인 cache[k][masking + (1 << k)]으로 바꾼 후, k번째 비트를 방문했다는 표시로 켜준뒤 전과 같은 연산을 반복적으로 진행하면 된다.


void printPath(long double distance){ int piv = 0, masking = 1; //cache 배열을 탐색해가며 다음 경로를 찾는다. for(int j = 0; j<=n;j++){ for(int k = 0; k <= n; k++){     if(masking&(1 << k)) continue;      if (distance - dist[piv][k] == cache[k][masking + (1 << k)]) {     //다음 경로 저장      path.push_back(k);     distance = cache[k][masking + (1 << k)];      piv = k; masking += (1 << k); } } } //경로 출력 for(int i=0; i<path.size();i++) printf("(%d)->",path[i]); printf("(0)"); }


+ Recent posts