题目描述
春节将近,小明想在节日期间逛一逛城里的 ( N ) 个著名景点。所有景点都能通过坐公交到达。需要设计一种公交路线方案,让小明能最快地逛完所有景点。
输入描述
第一行:一个整数 ( N ),表示景点数量,满足 ( 0 < N \leq 20 )。
接下来 ( N ) 行:每行包含 ( N ) 个元素,表示景点与景点之间的公交到达关系。如果 ( arr[i][j] = 1 ),表示景点 ( i ) 和景点 ( j ) 之间有直达公交相连;如果 ( arr[i][j] = 0 ),表示没有直达公交。
输出描述
最少乘坐公交的次数。如果不能逛完所有景点,则输出 0。
用例输入
样例 1
输入:
5
0 1 0 1 0
1 0 1 0 0
0 1 0 0 0
1 0 0 0 1
0 0 0 1 0
输出:
4
解题思路
问题分析
- 目标:找到一种方案,让小明能以最少的公交次数逛完所有景点。
- 关键点:
- 每个景点可以经过多次,公交线路也可以坐多次。
- 可以从任意景点开始游玩。
- 如果某些景点之间没有可达路径,则无法完成任务。
算法设计
-
数据结构:
- 使用二维数组
mp
存储景点之间的可达性。 - 使用动态规划数组
dp
,其中dp[state][j]
表示在已访问状态为state
的情况下,到达景点 ( j ) 的最少公交次数。
- 使用二维数组
-
预处理:
- 使用 Floyd-Warshall 算法计算任意两个景点之间的最短路径,确保所有可达性都被正确计算。
-
状态压缩动态规划:
- 使用状态压缩表示已访问的景点集合。状态
state
是一个二进制数,第 ( i ) 位为 1 表示景点 ( i ) 已访问。 - 初始化:对于每个景点 ( i ),
dp[1 << i][i] = 0
,表示从景点 ( i ) 开始的初始状态。 - 状态转移:对于每个状态
state
和当前景点 ( j ),尝试从state
中去掉 ( j ) 的状态pre
,并从pre
中的每个景点 ( k ) 转移到 ( j )。
- 使用状态压缩表示已访问的景点集合。状态
-
结果计算:
- 遍历所有景点,找到最终状态(所有景点都访问过)的最小值。
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int mp[25][25]; // 存储景点之间的可达性
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; // 景点数量
cin >> n;
// 读取景点之间的可达性
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> mp[i][j];
if (mp[i][j] == 0) {
mp[i][j] = INT_MAX / 2; // 无直达路径
}
}
}
// 使用 Floyd-Warshall 算法计算任意两个景点之间的最短路径
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
}
}
// 状态压缩动态规划
int state = 1 << n; // 总状态数
vector<vector<int>> dp(state, vector<int>(n, INT_MAX / 2));
// 初始化:每个景点作为起点
for (int i = 0; i < n; i++) {
dp[1 << i][i] = 0;
}
// 动态规划计算
for (int i = 0; i < state; i++) {
for (int j = 0; j < n; j++) {
// 如果当前状态不包含景点 j,则跳过
if ((i & (1 << j)) == 0) continue;
// 去掉景点 j 的状态
int pre = i ^ (1 << j);
for (int k = 0; k < n; k++) {
// 从 pre 中的状态转移
if ((i & (1 << k)) == 0) continue;
if (dp[pre][k] == INT_MAX / 2) continue;
dp[i][j] = min(dp[i][j], dp[pre][k] + mp[k][j]);
}
}
}
// 计算结果:找到最终状态的最小值
int res = INT_MAX / 2;
for (int i = 0; i < n; i++) {
res = min(res, dp[state - 1][i]);
}
// 输出结果
if (res == INT_MAX / 2) {
cout << 0; // 无法完成任务
} else {
cout << res; // 输出最少公交次数
}
return 0;
}