题目来源:蓝桥杯算法训练
知识点:搜索、Floyd算法
问题描述
逗志芃是有妹子的现充,但是有时候妹子就是烦恼。因为逗志芃太逗了,所以这段时间妹子对逗志芃发动了技能无理取闹,妹子要去玩很多的景点。由于逗志芃之前抽机花费了太多的时间,不久以后又要微积分考试了,所以现在被妹子搞成暴走状态了。但是妹子永远是上帝,所以逗志芃只能带妹子出去玩,不过为了节约时间,他希望找到一条花费时间最少的一次性游览线路。
输入格式
第一行1个数n,表示逗志芃所在的城市有多少个景点,接下来是一个n*n的矩阵。a(i,j)表示i号景点到j号景点的路上花费的时间是多少。
接下来是一个数m,表示逗志芃妹子要去去的景点数目。由于妹子在无理取闹,所以可能会有重复的景点,不过只要去一次就可以了。接下来是m个数,就是妹子要去的景点编号。
输出格式
一个数,最少的花费时间。
样例输入
3
0 1 2
1 0 3
2 3 0
3
2 3 1
样例输出
3
数据规模和约定
0<n<=30,0<m<=20,时间<=1000000
题目分析
本题可以看成是图问题中寻找最短路径的变体,只是在这里一个顶点可以经过多次。这里的图是一个完全图,数组a[i][j]
表示从地点i
到地点j
需要的时间。
可以发现,直接从地点i
到地点j
可能时间较长,而先从地点i
到地点k
,再从地点k
到地点j
能获得更短的时间。因此,在 DFS 搜索前先使用弗洛伊德算法对n*n
的矩阵进行处理,获得元素a[i][j]
的最小值。然后在图上进行 DFS 搜索即可。
关于弗洛伊德算法可以自行查找,本质就是本代码中preprocess()
函数的意思。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAX = 35;
int tim[MAX][MAX];
int des[MAX];
int vis[MAX];
int n, m, num;
int ans;
void preprocess() {
for(int k=1; k<=n; ++k){
for(int i=1; i<=n; ++i){
for(int j=1; j<=n; ++j){
tim[i][j] = min(tim[i][j],tim[i][k]+tim[k][j]);
}
}
}
}
void dfs(int cur, int cnt, int t) {
if(cnt == num) {
ans = min(ans, t);
return;
}
for(int i=1; i<=n; i++) {
if(des[i] && !vis[i] && tim[cur][i]+t <= ans) {
vis[i] = 1;
dfs(i, cnt+1, t+tim[cur][i]);
vis[i] = 0;
}
}
}
int main() {
cin >> n;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cin >> tim[i][j];
}
}
preprocess();
cin >> m;
int a;
num = 0;
memset(des, 0, sizeof(des));
for(int i=1; i<=m; i++) {
cin >> a;
if(!des[a]) {
num++;
des[a] = 1;
}
}
ans = 1e6;
for(int i=1; i<=n; i++) {
if(des[i]) {
memset(vis, 0, sizeof(vis));
vis[i] = 1;
dfs(i, 1, 0);
}
}
cout << ans << endl;
return 0;
}