题目描述
小明计划到某网红旅游景区进行一次“特种兵”旅游,景区有 ( N ) 个景点。请帮助小明规划一条游览路径,使得游览完所有景点花费的时间最短,以便安排返程时间。
输入描述
第一行,景点数量 ( N )。
接下来的 ( N+1 ) 行,每行 ( N+1 ) 个整数,以空格分隔,构成一个 ( (N+1) \times (N+1) ) 的矩阵。其中:
- 坐标 0 表示景区入口。
- ( G[0][j] ) 表示从景区入口到景区 ( j ) 的路程耗时。
- ( G[j][0] ) 表示从景区 ( j ) 到景区入口的路程耗时。
- ( G[i][j] ) 表示从景区 ( i ) 到景区 ( j ) 的路程耗时。
如果 ( i = j ),则 ( G[i][j] = 0 );如果景点 ( i ) 与景点 ( j ) 之间没有直接相通的道路,则 ( G[i][j] = G[j][i] = -1 )。从景区入口,一定可以到达每一个景点(直达或者经过其他景点)。
输出描述
游览完所有景点在路途上花费的最短时间。
用例输入
2
0 2 3
1 0 5
2 2 0
6
解题思路
这是一个典型的旅行商问题(TSP),目标是找到一条经过所有景点并返回起点的最短路径。由于景点数量 ( N ) 最多为 15,可以使用**状态压缩动态规划(状压DP)**来解决。
-
预处理最短路径:
- 使用 Floyd-Warshall 算法 预处理所有景点之间的最短路径。这样可以确保在后续的动态规划中,可以直接使用这些最短路径。
-
状态压缩动态规划:
- 使用一个二进制数
mask
来表示当前已经访问过的景点集合。例如,mask = 0b 101
表示已经访问了景点 1 和景点 3。 - 定义
dp[mask][cur]
表示访问了mask
标识的所有景点后,当前位于景点cur
的最小耗时。 - 初始化
dp[0][0] = 0
,表示从景区入口出发,初始状态没有访问任何景点。 - 状态转移:
- 对于每个状态
mask
和当前景点cur
,尝试访问下一个景点nex
。 - 如果
nex
是景区入口,则不改变mask
。 - 如果
nex
是其他景点,则更新mask
为mask | (1 << (nex - 1))
。 - 更新
dp[mask][cur]
的值为dp[mask][cur] + mp[cur][nex]
。
- 对于每个状态
- 最终结果:
- 需要访问所有景点并回到景区入口。
- 计算
dp[(1 << n) - 1][i] + mp[i][0]
,其中i
表示最后一个景点。
- 使用一个二进制数
代码
```cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include<sstream>
#include<bitset>
#include<stack>
#include<climits>
#include<iomanip>
#include<cstdint>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<vector<int>> mp(n + 1, vector<int>(n + 1));
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
cin >> mp[i][j];
}
}
// Floyd-Warshall算法预处理最短路径
for (int k = 0; k <= n; k++) {
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
if (mp[i][k] == -1 || mp[k][j] == -1) continue;
if (mp[i][j] == -1) {
mp[i][j] = mp[i][k] + mp[k][j];
} else {
mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
}
}
}
// 状态压缩动态规划
vector<vector<int>> dp(1 << n, vector<int>(n + 1, INT_MAX));
dp[0][0] = 0; // 从景区入口出发
for (int mask = 0; mask < (1 << n); mask++) {
for (int cur = 0; cur <= n; cur++) {
if (dp[mask][cur] != INT_MAX) {
for (int nex = 0; nex <= n; nex++) {
if (mp[cur][nex] == -1) continue;
if (nex == 0) {
// 去入口不改变mask
dp[mask][nex] = min(dp[mask][nex], dp[mask][cur] + mp[cur][nex]);
} else {
int nex_mask = mask | (1 << (nex - 1));
dp[nex_mask][nex] = min(dp[nex_mask][nex], dp[mask][cur] + mp[cur][nex]);
}
}
}
}
}
// 最终需要访问所有景点,并回到入口
int end = (1 << n) - 1;
int res = INT_MAX;
for (int i = 0; i <= n; i++) {
if (dp[end][i] != INT_MAX && mp[i][0] != -1) {
res = min(res, dp[end][i] + mp[i][0]);
}
}
cout << res;
}