多起点多终点--建立虚拟源点 最短路劲“数”
选择最佳线路 https://www.acwing.com/problem/content/1139/
拓扑序
spfa不可以 但可以做负权 先求最短路 在找
bfs可以
djistkra可以
最短路计数https://www.acwing.com/problem/content/1136/
1.最短路满足条件 是不能存在值为0的环
2.可以吧图抽象成最短路树 保证每次被更新的点的父节点已经是完全更新了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5+10,M=4e5+5,mod=100003 ;
int h[N],ne[M],e[M],idx;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int d[N],cnt[N];
void bfs(){
queue<int>q;
q.push(1);
memset(d, 0x3f, sizeof d);
d[1]=0;
cnt[1]=1;
while(q.size()){
auto t=q.front();
q.pop();
for (int i = h[t]; ~i ; i=ne[i] ){
auto j=e[i];
if(d[j]>d[t]+1){
d[j]=d[t]+1;
cnt[j]=cnt[t];
q.push(j);
} else if(d[j]==d[t]+1){
// d[j]=d[t]+1;
cnt[j]=(cnt[t]+cnt[j])%mod;
}
}
}
}
int main()
{
int n,m;
cin >>n>>m;
memset(h, -1, sizeof h);
while(m--){
int a,b;cin>>a>>b;
add(a, b);
add(b ,a);
}
bfs();
for (int i = 1; i <= n; i ++ ){
cout << cnt[i]<<endl;
}
return 0;
}
观光https://www.acwing.com/problem/content/385/
1.设状态dist[i][0,1]dist[i][0,1]表示初始城市SS到城市ii的最短距离和次短距离,cnt[i][0,1]cnt[i][0,1]表示城市SS到城市ii的最短路径和次短路经的条数
2.初始时,dist[S][0]dist[S][0]为0,cnt[S][0]cnt[S][0]为1(其余都初始化成正无穷,初始时S不存在次短路)
3.0为最短 j为此段
Dijkstra算法枚举城市t可通往的城市j时,有四种情况:
queue
dist[j][0]>dist[v][type]+w[i] 也就是最短路可以被更新 当前最短路变成次短路,更新最短路,将最短路和次短路加入优先队列 cnt[j][0]=cnt[t][type]//这里type注意是当前的t节点的type 需要找到那个指定点
dist[j][0]=dist[v][type]+w[i]找到一条新的最短路,更新最短路条数 cnt[j][0]+=cnt[t][type]
dist[j][1]>dist[v][type]+w[i]找到一条更短的次短路,覆盖掉当前次短路,加入优先队列 cnt[j][1]=cnt[t][type]
dist[j][1]=dist[v][type]+w[i]找到一条新的次短路,更新次短路条数 cnt[j][1]+=cnt[t][type]
4.到F城市的次短路径如果比最短路径恰好多1,则把这样的路径条数加到答案里
5.C++优先队列大根堆需要重载小于号,小根堆需要重载大于号
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 1010,M = 10010;
int h[N],e[M],ne[M],w[M],idx;
//状态0表示的是求最下,状态1表示求的是次小
int cnt[N][2]; //cnt[i][0]表示当前到达节点是i,且求的是0状态下的所有路径中最短路径的边数
int dist[N][2]; //dist[i][0]表示当前到达节点是i,且求的是0状态下的最短路径的值
bool st[N][2]; //与上面同理
int n,m,S,T; //S表示起点,T表示终点
struct node{ //小根堆,重载大于号
int id,type,distance; //分别是编号,状态,和当前点到起点的最小或次小距离
bool operator> (const node& a) const{ //从大到小排序
return distance > a.distance;
}
};
void add(int a,int b,int c){
w[idx] = c;
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int dijkstra(){
memset(st, 0, sizeof st);
memset(dist, 0x3f, sizeof dist);
memset(cnt, 0, sizeof cnt);
priority_queue<node,vector<node>,greater<node>> heap;
dist[S][0] = 0;
cnt[S][0] = 1;
heap.push({S,0,0});//(节点,类型,距离)
while(heap.size()){
node t = heap.top();
heap.pop();
int ver = t.id , type = t.type , distance = t.distance;
if(st[ver][type]) continue;
st[ver][type] = true;
for(int i = h[ver];i != -1;i = ne[i]){
int j = e[i];
//先考虑最短的情况(大于、等于)
if(dist[j][0] > dist[ver][type] + w[i]){
//dist[j][0]成为次小,先要赋值给dist[j][]中次小的状态
dist[j][1] = dist[j][0]; cnt[j][1] = cnt[j][0];
heap.push({j, 1, dist[j][1]}); //距离发生改变就要入队
dist[j][0] = dist[ver][type] + w[i]; cnt[j][0] = cnt[ver][type]; //直接转移
heap.push({j,0,dist[j][0]});////距离发生改变就要入队
}else if(dist[j][0] == dist[ver][type] + w[i]){
cnt[j][0] += cnt[ver][type]; //从t经过的最短路,在j上经过的时候也是最短路
//轮到枚举次小
}else if(dist[j][1] > dist[ver][type] + w[i]){
dist[j][1] = dist[ver][type] + w[i];
cnt[j][1] = cnt[ver][type];
heap.push({j, 1, dist[j][1]});
}else if(dist[j][1] == dist[ver][type] + w[i]){
cnt[j][1] += cnt[ver][type]; //从t经过的最短路,在j上经过的时候也是最短路
}
}
}
int res = cnt[T][0];
//最后还要特判以下最小和次小的路径之间是否相差1符合要求
if (dist[T][0] + 1 == dist[T][1]) res += cnt[T][1];
return res;
}
int main(){
int t;
cin >> t;
while(t--){
memset(h,-1,sizeof h);
cin >> n >> m;
for(int i = 0;i < m;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
scanf("%d%d",&S,&T);
cout << dijkstra() << endl;
}
return 0;
}