大致题意
图中有 N 座城市,M 条长为 L 的有向路径,求城市 S 到城市 F 间最短路和比最短路长 1 个单位的路径的数量和,该数字最多达到 109。2 ≤ N ≤ 1,000, 1 ≤ M ≤ 10, 000, 1 ≤ L ≤ 1,000
基本思路是分别求图中最短路和次短路的数量,然后判断最短路与次短路长度是否差值为 1。
通过修改 dijkstra 算法,不断更新最短路和次短路,在O(MlogN) 内求解。有点类似 POJ 3255 求解次短路的长度的做法,这里需要修改一下。统计路径数量,可以 dfs 路径还原做,但这里复杂度感人。那么可以在 dijkstra 算法中一边更新路径值,一边统计路径数。
统计路径数量,对于更新的结点,到该节点的路径数为前驱结点记录的路径数;对于不能更新,但与当前保存的值相等的情况,到该节点的路径数加上前驱结点的路径数。
对于优先队列的优化,参照白书 POJ 3255 的做法 link ,对于当前出队的值,若果大于当前保存的次短路的值,则表示该节点已经被更新过,不予处理。
if(cost > dist[v][1]) continue;
然后开始WA得着迷… dijkstra 的基础是图中没有负边,找到最短路已被确定的顶点,从它出发更新相邻顶点的最短距离,此后不需要再关心该节点,路径数量统计也不会重复,次短路同理。
问题在于,最短路和次短路同时处理,由最短路松弛的顶点可能会重复次短路的数量统计,即某一个值被分别作为最短路以及次短路入队的情况。试试这组数据就明白了,实际最短路和次短路数量都是 1,但跑出来次短路数量是 2。
1
5 5
1 2 1
2 4 7
1 3 2
3 4 4
4 5 1
1 5
为了防止路径重复,要分别记录当前是松弛的最短路或次短路,一旦某个顶点的最短路或次短路确定,之后就不再考虑。
#include <cstdio>
#include <STDLIB.H>
#include <algorithm>
#include <iostream>
#include<functional>
#include <queue>
#include <vector>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define eps 1e-5
#define M_PI 3.14159265358979323846
#define MAX_E 10000
#define MAX_V 1005
using namespace std;
struct update{
int cost, v, f;
update(int cost, int v, int f) : cost(cost), v(v), f(f) {}
};
bool operator < (const update &a, const update &b){
return a.cost > b.cost;
}
struct edge{
int to, cost;
edge(int to, int cost) : to(to), cost(cost) {}
};
int V, E, S, F;
vector<edge> G[MAX_V];
int dist[MAX_V][2], cnt[MAX_V][2];
int vis[MAX_V][2];
//邻接表建图
void init(){
scanf("%d%d", &V, &E);
for(int i = 0; i < V; i++) vector<edge> ().swap(G[i]);
for(int i = 0; i < E; i++){
int a, b, l;
scanf("%d%d%d", &a, &b, &l);
G[a - 1].push_back(edge(b - 1, l));
}
scanf("%d%d", &S, &F);
--S, --F;
}
int dijksrtra(){
priority_queue<update> que;
memset(dist, 0x3f, sizeof(dist));
memset(vis, 0, sizeof(vis));
dist[S][0] = 0, cnt[S][0] = 1;
que.push(update(0, S, 0));
while(!que.empty()){
update p = que.top(); que.pop();
int v = p.v, cost = p.cost;
//if(cost > dist[v][1]) continue;//错误的优化
//最短路或次短路已确定,就不用再考虑
if(vis[v][p.f]) continue;
vis[v][p.f] = 1;
for(int i = 0; i < G[v].size(); i++){
edge &e = G[v][i];
int u = e.to, d = cost + e.cost, n = cnt[v][p.f];
//更新最短路或次短路,并统计路径数量
if(d < dist[u][0]){
swap(d, dist[u][0]);
swap(n, cnt[u][0]);
que.push(update(dist[u][0], u, 0));
}
else if(d == dist[u][0]){
cnt[u][0] += n;
continue;
}
//排除不满足条件的次短路
if(d > dist[u][0] + 1) continue;
if(d < dist[u][1]){
dist[u][1] = d, cnt[u][1] = n;
que.push(update(dist[u][1], u, 1));
}
else if(d == dist[u][1]){
cnt[u][1] += n;
}
}
}
return cnt[F][0] + ((dist[F][1] == dist[F][0] + 1) ? cnt[F][1] : 0);
}
int main(){
int t;
scanf("%d", &t);
while(t--){
init();
printf("%d\n", dijksrtra());
}
return 0;
}
来源:CSDN
作者:neweryyy
链接:https://blog.csdn.net/neweryyy/article/details/104547570