先仔细梳理一下优先队列优化的dijkstra算法思路:
我们要定义一个结构体,包含终点v,和从源点到终点v的最短路径长度d
struct node {
int v, d;
node(){}
node(int v, int d) :v(v), d(d) {}
//由于优先队列的特点重载<操作符,使得d最小的排在队列的front
bool operator<(const node&op)const {
return d > op.d;
}
};
然后最重要的地方就是:我们每次从优先队列里取出的元素如果点v没被标记,那么从源点到点v的最短路就是d了。注意优先队列里对于一个顶点v可能有好多node对应着不同的d,但是每次我们最先取出来的是d最小的,如果此时v没被标记过的话,我们就找到了v的最短路,但是如果此时v已经标记过了,那么我们就已经找过了v的最短路,此时找的d是>=最短路的,所以我们就不用处理这个node。
下面是完整优先队列优化的dijkstra代码:
struct node {
int v, d;
node(){}
node(int v, int d) :v(v), d(d) {}
//由于优先队列的特点重载<操作符,使得d最小的排在队列的front
bool operator<(const node&op)const {
return d > op.d;
}
};
int fst[N], nxt[M], to[M], cost[M], e;
int dis[N];
bool vis[N];//vis[u]表示我们得到u到源点s的最短路之前为false,之后为true
void add(int u, int v, int c) {
to[e] = v;
cost[e] = c;
nxt[e] = fst[u];
fst[u] = e++;
}
void dijkstra(int s) {
memset(dis, 0x3f, sizeof dis);
memset(vis, false, sizeof vis);
dis[s] = 0;
priority_queue<node>q;
q.push(node(s, 0));
while (!q.empty()) {
node now = q.top(); q.pop();
int u = now.v;
if (vis[u])continue;//在这之前已经找到了点u的最短路
vis[u] = true;//此时找到了到点u的最短路
//dis[u] = now.d;//这句有没有无所谓
for (int i = fst[u]; i != -1; i = nxt[i]) {
int v = to[i], c = cost[i];
if (dis[v] > now.d + c) {
dis[v] = now.d + c;
q.push(node(v, dis[v]));
}
}
}
}
最后来说一下poj 3463这个题,题目让求最短路和比最短路长1的路径个数。考虑dijkstra算法的话,把dis和vis数组都换成二维的dis[N][2],vis[N][2],分别表示最短路和次短路,然后用cnt[N][2]来分别记录最短路和次短路的路径个数。然后用dijkstra算法优先队列优化一下就OK了。注意对于取出来的元素我们要知道他是最短路还是次短路,所以结构体要加一个变量表示最短还是次短。
稍微复杂一点的地方就是更新最短路或次短路条件的判断,要分四种情况。
下面是ac代码:
#pragma warning(disable:4996)
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1005;
const int M = 10005;
int fst[N], nxt[M], to[M], cost[M], e;
int S, F, n, m;
int dis[N][2], cnt[N][2];
bool vis[N][2];
struct node {
int type;//标记最短路->0,还是次短路->1
int v;//标记终点
int len;//标记S->v路径长度
node(){}
node(int type, int v, int len) :type(type), v(v), len(len) {}
bool operator<(const node&op)const {
return len > op.len;
}
};
priority_queue<node>q;
void init() {
while (!q.empty())q.pop();
e = 0;
memset(fst, -1, sizeof fst);
}
void add(int u, int v, int c) {
to[e] = v;
cost[e] = c;
nxt[e] = fst[u];
fst[u] = e++;
}
int dijkstra() {
memset(dis, 0x3f, sizeof dis);
memset(vis, false, sizeof vis);
memset(cnt, 0, sizeof cnt);
dis[S][0] = 0;
//vis[S][0] = true;
cnt[S][0] = 1;
q.push(node(0, S, 0));
while (!q.empty()) {
node now = q.top(); q.pop();
if (vis[now.v][now.type])continue;//找到下一个未访问的node
vis[now.v][now.type] = true;//此时最短路或次短路标记访问完成
int u = now.v;
for (int i = fst[u]; i != -1; i = nxt[i]) {
int v = to[i], c = cost[i];
int d = now.len + c;
if (d < dis[v][0]) {//此时最短路,次短路都换
dis[v][1] = dis[v][0];
cnt[v][1] = cnt[v][0];
dis[v][0] = d;
cnt[v][0] = cnt[u][now.type];
q.push(node(0, v, dis[v][0]));
q.push(node(1, v, dis[v][1]));
}
else if (d == dis[v][0]) {//此时最短路条数增加(不只+1)
cnt[v][0] += cnt[u][now.type];;
}
else if (d < dis[v][1]) {//此时次短路换
dis[v][1] = d;
cnt[v][1] = cnt[u][now.type];
q.push(node(1, v, dis[v][1]));
}
else if (d == dis[v][1]) {//此时次短路条数增加
cnt[v][1]+= cnt[u][now.type];
}
}
}
if (dis[F][1] - dis[F][0] <= 1)return cnt[F][1] + cnt[F][0];
return cnt[F][0];
}
int main() {
//freopen("in.txt", "r", stdin);
int t; scanf("%d", &t);
while (t--) {
init();
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v, c; scanf("%d %d %d", &u, &v, &c);
add(u, v, c);
}
scanf("%d %d", &S, &F);
printf("%d\n", dijkstra());
}
return 0;
}