加载中...

多起点多终点--建立虚拟源点 最短路劲“数”

选择最佳线路 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;
}

posted @ 2022-05-05 18:09  liang302  阅读(69)  评论(0编辑  收藏  举报