题目链接:传送门
题目大意:
给出n<=100的有向图,和路径p,求p的最短子序列v,使得依次经过v中所有点的路径为p。
思路:
题意其实就是让我们求路径上的一些关键点v,对于所有的关键点:vi到vi+1的最短路的长度,等于vi到vi+1这两个点在序列p中的下标的差,且vi到vi+2的最短路的长度,小于vi到vi+2在序列p中的下标的差。
如果用dis[u][v] 表示:在题目给出的有向图中,从u出发到v的最短路径的长度。则:
1、p1和pm都是关键点。
2、假设u是之前的最后一个关键点,如果dis[u][pi+1] != dis[u][pi] + 1,则pi就是下一个关键点。
那么用Floyd预处理任意两点间的最短路,就可以O(m)地跑一遍数组p,得到所有的关键点,就是答案了。
代码:O(n3+m)
#include <bits/stdc++.h> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define N 105 #define M 1000005 #define INF 0x3f3f3f3f #define mk(x) (1<<x) // be conscious if mask x exceeds int #define sz(x) ((int)x.size()) #define lson(x) (x<<1) #define rson(x) (x<<1|1) #define mp(a,b) make_pair(a, b) #define endl '\n' #define lowbit(x) (x&-x) using namespace std; typedef long long ll; typedef double db; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-'0', ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } int n, m; int a[N][N]; bool cant[N][N]; int p[M]; void Folyd() { memset(cant, false, sizeof cant); for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) if (i != k) { for (int j = 1; j <= n; j++) if (j != i && j != k) { if (a[i][j] > a[i][k] + a[k][j]) a[i][j] = a[i][k] + a[k][j]; else if (a[i][j] == a[i][k] + a[k][j]) { cant[i][j] = true; } } } } } vector <int> ans; int main() { read(n); for (int i = 1; i <= n; i++) { string s; cin >> s; for (int j = 1; j <= n; j++) { if (j == i) a[i][j] = 0; else if (s[j-1] == '1') a[i][j] = 1; else a[i][j] = 1e5; } } read(m); for (int i = 1; i <= m; i++) { read(p[i]); } Folyd(); int u = p[1]; ans.push_back(u); for (int i = 2; i <= m; i++) { if (a[u][p[i]] > a[u][p[i-1]]) continue; u = p[i-1]; ans.push_back(u); } ans.push_back(p[m]); cout << ans.size() << endl; for (int i = 0; i < sz(ans); i++) { printf("%d%c", ans[i], i == sz(ans)-1 ? '\n' : ' '); } return 0; }View Code