题意:给一个无权有向图,和一个行走序列,求出一个最短的序列,该序列需满足:
- 序列为行走序列的子序列,且起点及终点与行走序列相同
- 走该序列所经过的最短路之一为给定行走序列
思路:可以看出行走序列中一个点可以省略,当且仅当其前驱和后继之间的最短路必须经过该点;若前驱和后继不经过该点,表明有不经过该点的更短的路径,故必须将其加入答案序列,否则就不是给定序列的子序列。需要注意的是若前驱和后继为同一个点,则也必须加入答案序列(总不能停在一个点不走吧)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 100+5;
int n, m, g[maxn][maxn], p[1000005], res[1000005];
char s[maxn];
const int inf = 0x3f3f3f3f;
int main()
{
cin >> n;
memset(g, inf, sizeof(g));
for (int i = 1; i <= n; i++) {
scanf("%s", s+1);
for (int j = 1; j <= n; j++) {
if (s[j] == '1')
g[i][j] = 1;
}
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (g[i][j] > g[i][k] + g[k][j])
g[i][j] = g[i][k] + g[k][j];
cin >> m;
for (int i = 0; i < m; i++)
cin >> p[i];
int pre = 0, cnt = 0;
res[cnt++] = p[0];
for (int i = 1; i < m-1; i++) {
if (g[p[pre]][p[i+1]] != g[p[pre]][p[i]] + g[p[i]][p[i+1]] || p[pre] == p[i+1]) {
res[cnt++] = p[i];
pre = i;
}
}
res[cnt++] = p[m-1];
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++)
printf("%d ", res[i]);
printf("\n");
return 0;
}