哈密顿路径,通过每个节点一次且仅且一次。

哈密顿回路(旅行商问题),通过每个节点一次且仅且一次,最终回到起点。

访问所有节点的最短路径

NP,无多项式解。编码bfs的”状态节点“,图遍历。

int shortestPathLength(vector<vector<int>>& graph) {
    // 编码所有节点的访问状态到二进制covered,当前节点为cur
    // bfs的"状态"为 (covered,cur)
    const int N = graph.size();
    const int VISIT_ALL = (1 << N) - 1;

    vector<vector<bool>> visited(1 << N, vector<bool>(N, false));
    queue<array<int, 2>> q;
    for (int i = 0; i < N; i++) {
        q.push({1 << i, i});
    }

    int dist = 0;
    while (!q.empty()) {
        for (int sz = q.size(); sz > 0; sz--) {
            auto [covered, cur] = q.front(); q.pop();                
            if (visited[covered][cur]) continue;
            visited[covered][cur] = true;

            if (covered == VISIT_ALL) return dist;
            for (auto next : graph[cur]) {
                q.push({covered | (1 << next), next});
            }
        }
        dist++;
    }
    return -1;
}

或者dp解法

class Solution {
    const int INF = 1e7;
public:
    int shortestPathLength(vector<vector<int>>& graph) {
        const int N = graph.size();
        vector<vector<int>> dist(N, vector<int>(N, INF));
        for (int i = 0; i < N; i++) {
            for (int j : graph[i]) {
                dist[i][j] = 1;
            }
        }
        
        floyd(dist);        
        return shortestHamilton(dist);
    }
    
    void floyd(vector<vector<int>> &dist) {
        // 求所有点对最短距离
        const int N = dist.size();
        for (int k = 0; k < N; k++) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
    }
    
    int shortestHamilton(vector<vector<int>> &dist) {
        // dp求哈密顿路径
        // 设dp[group][dst]表示经过集合group中的所有节点、
        // 并最终停在group中dst节点的最短路径,
        // 在group中选一节点u、在group外选一节点v,u松弛v
        // dp[group+{v}][v] = min( dp[group][u] + dist[u][v] )
        // 其中dist[u][v]表示u->v的最短路径,可用Floyd-Warshall算法三重循环松弛得到
        const int N = dist.size();
        vector<vector<int>> dp(1<<N, vector<int>(N, INF));
        for (int i = 0; i < N; i++) {
            dp[1<<i][i] = 0;
        }
        for (int group = 1; group < (1<<N); group++) {
            // group中选一点u,group外选一点v,u松弛v
            for (int u = 0; u < N; u++) {
                int umask = 1<<u;
                if (!(group & umask)) continue;
                for (int v = 0; v < N; v++) {
                    int vmask = 1<<v;
                    if (group & vmask) continue;
                    dp[group|vmask][v] = min(dp[group|vmask][v],
                                             dp[group][u] + dist[u][v]);
                }
            }
        }
        
        // 从所有点出发的最小值
        int ans = INF;
        for (int i = 0; i < N; i++) {
            ans = min(ans, dp[(1<<N)-1][i]);
        }
        return ans;    
    }
};

也有这么写dp的

int shortestHamilton(vector<vector<int>> &dist) {
    // dp求哈密顿路径
    // 设dp[group][dst]表示经过集合group中的所有节点、
    // 并最终停在group中dst节点的最短路径,
    // 在group中选一节点u、在group-{u}中选一点k,k松弛u
    // dp[group][u] = min( dp[group-{u}][k] + dist[k][u] )
    // 其中dist[u][v]表示u->v的最短路径,可用Floyd-Warshall算法三重循环松弛得到
    const int N = dist.size();
    vector<vector<int>> dp(1<<N, vector<int>(N, INF));
    for (int i = 0; i < N; i++) {
        dp[1<<i][i] = 0;
    }
    for (int group = 1; group < (1<<N); group++) {
        // group中选一点u、group-{u}中选一点k,k松弛u
        for (int u = 0; u < N; u++) {
            int umask = 1<<u;
            if (!(group & umask)) continue;
            for (int k = 0; k < N; k++) {
                int kmask = 1<<k;
                if (!(group ^ umask) & kmask) continue;
                dp[group][u] = min(dp[group][u],
                                    dp[group^umask][k] + dist[k][u]);
            }
        }
    }
    
    // 从所有点出发的最小值
    int ans = INF;
    for (int i = 0; i < N; i++) {
        ans = min(ans, dp[(1<<N)-1][i]);
    }
    return ans;    
}

旅行商问题

初始化dp[1][0]=0(假设起点为0),先求最短哈密顿路径,最后

    // 哈密顿环从顶点0出发、回到顶点0
    int ans = INF;
    for (int i = 0; i < N; i++) {
        ans = min(ans, dp[(1<<N)-1][i] + dist[i][0]);
    }