bzoj1138
dp+spfa优化
最朴素的dp是dp[i][j]表示i->j的最短路,然后把所有pair(i,i)放到队列里跑spfa,但是这样被卡掉了,那么我们要优化一下
问题在于每次我们转移的时候要枚举i和j的邻居,这样会被两个连起来的菊花卡掉,那么我们希望一次只走一步,那么复杂度会大大降低,于是我们设一个状态g[i][j][k],表示当前在i,j,上一条出边的字符为k,这样我们让f和g交替转移,达到了每次只走一步的目标,然后就能过了,原先每次转移最坏O(m*m)?现在大概是O(n*m)的转移,状态数O(n*n*26)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N = 410, inf = 0x3f3f3f3f; struct edge { int nxt, to, c; } e[N * N << 1]; struct node { int u, v, c; node(int u, int v, int c) : u(u), v(v), c(c) {} }; int n, m, cnt = 1, q, last; int head1[N], head2[N], f[N][N], g[N][N][27], vis[N][N][27]; void link1(int u, int v, int c) { e[++cnt].nxt = head1[u]; head1[u] = cnt; e[cnt].to = v; e[cnt].c = c; } void link2(int u, int v, int c) { e[++cnt].nxt = head2[u]; head2[u] = cnt; e[cnt].to = v; e[cnt].c = c; } void bfs() { queue<node> q; memset(f, inf, sizeof(f)); memset(g, inf, sizeof(g)); for(int i = 1; i <= n; ++i) vis[i][i][26] = 1, f[i][i] = 0, q.push(node(i, i, 26)); for(int i = 1; i <= n; ++i) for(int j = 0; j < 26; ++j) vis[i][i][j] = 1, g[i][i][j] = 0, q.push(node(i, i, j)); while(!q.empty()) { node o = q.front(); q.pop(); int u = o.u, v = o.v; vis[u][v][o.c] = 0; if(o.c == 26) { for(int i = head1[v]; i; i = e[i].nxt) if(g[u][e[i].to][e[i].c] > f[u][v] + 1) { g[u][e[i].to][e[i].c] = f[u][v] + 1; if(!vis[u][e[i].to][e[i].c]) { vis[u][e[i].to][e[i].c] = 1; q.push(node(u, e[i].to, e[i].c)); } } } else { for(int i = head2[u]; i; i = e[i].nxt) if(e[i].c == o.c && f[e[i].to][v] > g[u][v][o.c]) { f[e[i].to][v] = g[u][v][o.c] + 1; if(!vis[e[i].to][v][26]) { vis[e[i].to][v][26] = 1; q.push(node(e[i].to, v, 26)); } } } } } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i) { int u, v; char c[2]; scanf("%d%d%s", &u, &v, c); link1(u, v, c[0] - 'a'); link2(v, u, c[0] - 'a'); } bfs(); scanf("%d%d", &q, &last); q--; while(q--) { int x; scanf("%d", &x); printf("%d\n", f[last][x] == inf ? -1 : f[last][x]); last = x; } return 0; }