TSP问题

这种类型的题很重要,但没有找到OJ,所以主要放一下老师的讲义,以及根据讲义所写的代码。真的遇到这种题能写出来就行了。

描述

N个城市,编号1到N,起点是1,终点是N(N<=16)。

任意两个城市间都有路,A到B和B到A的路可能不一样长。

已知所有路的长度,问经每个城市恰好一次的最短路径的长度。

解法

dp[s][j]表示经过集合s中的每个点恰好一次,且最后走的点是j的最佳路径的长度。

状态转移方程:

dp[s][j] = min { dp[s-j][k] + w[k][j] | k∈s-j },枚举每一个k,算出最小值

边界条件:

dp[{i}][i] = 0

状态压缩的关键:用short变量表示s,对应位为1表示这个点在这个集合中,对应位为0表示这个点不在这个集合中。

所有点:(1<<n)-1

去掉一个点j:s&(~(1<<j>))或者s-(1<<j)

最终时间复杂度:O(n*n*2^n)

代码如下:由于没有OJ,正确性没有得到验证。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define INF 1<<30
 5 using namespace std;
 6 int w[17][17];//w[i][j]表示从i号城市到j号城市路的长度,变化从0开始
 7 int dp[(1 << 16)][17];
 8 int main() {
 9     int N;
10     cin >> N;
11     memset(dp, 0, sizeof(dp));
12     for (int i = 0; i < N; i++)
13         for (int j = 0; j < N; j++)
14             cin >> w[i][j];
15     for (int j = 0; j < N; j++)
16         dp[1 << j][j] = 0;
17     for (int s = 0; s < 1 << N; s++) {
18         for (int j = 0; j < N; j++) {
19             if ((s >> j) & 1) {//如果j在s集合中
20                 int s_j = s - (1 << j);
21                 if (s_j != 0) {//避免破坏初始设置的边界条件
22                     dp[s][j] = INF;
23                     for (int k = 0; k < N; k++) {//枚举k
24                         if ((s_j >> k) & 1) {
25                             dp[s][j] = min(dp[s][j], dp[s_j][k] + w[k][j]);
26                         }
27                     }
28                 }
29             }
30         }
31     }
32     int result = dp[(1<<N)-1][0];
33     for (int j = 1; j < N; j++)
34         result = min(result, dp[(1 << N) - 1][j]);
35     cout << result << endl;
36     return 0;
37 }

 

posted @ 2021-07-10 11:20  永远是个小孩子  阅读(70)  评论(0编辑  收藏  举报