외판원 순회

Problem

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

Summary

한 외판원이 어느 한 도시에서 출발해 N개의 도시를 모두 거쳐 다시 원래의 도시로 돌아오는 순회 여행 경로
중 가장 적은 비용을 들이는 여행 경로구하기 도시의 수 N (2<=N<=16)

Idea

각 도시를 전체 순회해보는 방법은 N! 가지가 된다. 10!는3628800 이며 11!는 39916800이다.

전체 탐색

static private void TSP(int curr, ArrayList<Integer> path, int sum) {
    if (path.size() == N) {
        int ret = W[curr][path.get(0)];
        if (ret == 0) {
            return;
        }
        sum += ret;
        if (min > sum) {
            min = sum;
        }
        return;
    }

    for (int i = 0; i < N; i++) {
        if (curr == i || W[curr][i] == 0) {
            continue;
        }

        if (visited[i] == false) {
            visited[i] = true;
            path.add(i);
            TSP(i, path, sum + W[curr][i]);
            visited[i] = false;
            path.remove(path.size()-1);
        }
    }
}

DP

방문도시 1->2->3->4->5 와 1->2->3->5->4 로 순회 시 1->2->3은 이미 방문했던 길로 중복 체크 될 수 있다

f(N (시작점)에서 출발해서 모든 도시를 방문할 때 드는 최소 비용.
f(N, {N-1,N-2….1}) = cost(N,N-1) + f(N-1, {N-2,N-3,….1})

dp[1][0000 1110(=14)] = f(1, {2,3,4}) ==> 도시 1에서 2,3,4를 순회하는 최소비용
dp[2][0000 1101(=13)] = f(2, {1,3,4}) ==> 도시 2에서 1,3,4를 순회하는 최소비용

visited 표현 시 bit operation 활용

int min = Integer.MAX_VALUE;
for (int i = 1 ; i <= N; i++) {
    end = i;
    min = Math.min(min, TSP(i, 1 << (i-1)));
}

static private int TSP(int curr, int visited) {

    if (visited == (1<< (N+1)) - 1) {
        return W[curr][end];
    }
    int ret = 0;
    ret = cached[curr][visited];
    if (ret != 0) {
        return ret;
    }

    ret = Integer.MAX;

    for (int i = 1; i <= N; i++) {
        if (curr == i || W[curr][i] == 0) {
            continue;
        }
        if ((visited & (1 << i)) > 0) {
            continue;
        }

        //ret = Math.min(ret, TSP(i, visited | 1 << i) + W[curr][i]);
        if (ret > TSP(i, visited | 1 << i) + W[curr][i]) {
            ret = TSP(i, visited | 1 << i) + W[curr][i];
        }
    }
    cached[curr][visited] = ret;
    return cached[curr][visited];
}

부분 문제 수: N(노드수) * 2^N (방문체크), tsp 내에서의 반복 = N^2 * 2^N

경로 추적