最短路计数与次短路计数

最短路计数

AcWing 1134. 最短路计数

题目链接
https://www.acwing.com/problem/content/1136/

解析

  • n个点m条边的无向无权图
  • 通过dp递推记录最短路条数
  • 需证明求最短路的过程中可以递推地计数,可以证明用dijkstra和bfs求最短路的时候点的扩展是拓扑图,可以进行递推计数
  • 条件限制:(好像是不能有权值为0的环?????还是边权非负啊???待确认。。。。)
  • 本题套用bfs的板子

Ac代码

点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e5 + 10, M = 4e5 + 10, mod = 100003;

int h[N], e[M], ne[M], idx;
int n, m;
int dist[N], cnt[N];
queue<int> q;

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void bfs(){
    memset(dist, 0x3f, sizeof dist);
    dist[1]= 0, cnt[1] = 1;
    q.push(1);
    
    while(q.size()){
        auto t = q.front();
        q.pop();
        
        for(int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if(dist[j] > dist[t] + 1){
                dist[j] = dist[t] + 1;
                cnt[j] = cnt[t];
                q.push(j);
            }
            else if(dist[j] == dist[t] + 1) cnt[j] = (cnt[j] + cnt[t]) % mod;
        }
    }
    return;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    while(m --){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    bfs();
    for(int i = 1; i <= n; i ++) printf("%d\n", cnt[i]);
    
    return 0;
}



次短路计数

AcWing 383. 观光

题目链接
https://www.acwing.com/problem/content/385/

解析

  • 本题主要用了拆点和最短路计数
  • 本题要同时进行最短路计数和次短路计数,均采用递推思想,可以在bfs或者dijkstra的同时完成
  • 由于最短路和次短路的更新都会影响未来点的最短路和次短路的更新,所以要拆点,拆点方法就是放弃传统dijkstra在堆中维护pii的想法,在堆中存储自定义的struct并重载运算符,然后递推更新答案
  • 记录和更新最短路和次短路的代码写法较为固定,可以学习

Ac代码

点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1010, M = 1e4 + 10;

struct Ver{
    int id, type, d;
    bool operator > (const Ver &W) const{
        return d > W.d;
    }
};

int n, m, S, T;
int h[N], e[M], ne[M], w[M], idx;
int dist[N][2], cnt[N][2];
bool st[N][N];

void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

int dijkstra(){
    memset(cnt, 0, sizeof cnt);
    memset(st, 0, sizeof st);
    memset(dist, 0x3f, sizeof dist);
    dist[S][0] = 0, cnt[S][0] = 1;
    
    priority_queue<Ver, vector<Ver>, greater<Ver>> heap;
    heap.push({S, 0, 0});
    
    while(heap.size()){
        Ver t = heap.top();
        heap.pop();
        
        int ver = t.id, type = t.type, distance = t.d, count = cnt[ver][type];
        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] > distance + w[i]){
                dist[j][1] = dist[j][0];
                cnt[j][1] = cnt[j][0];
                heap.push({j, 1, dist[j][1]});
                
                dist[j][0] = distance + w[i];
                cnt[j][0] = count;
                heap.push({j, 0, dist[j][0]});
            }
            else if(dist[j][0] == distance + w[i]) cnt[j][0] += count;
            else if(dist[j][1] > distance + w[i]){
                dist[j][1] = distance + w[i];
                cnt[j][1] = count;
                heap.push({j, 1, dist[j][1]});
            }
            else if(dist[j][1] == distance + w[i]) cnt[j][1] += count;
        }
    }
    int res = cnt[T][0];
    if(dist[T][1] == dist[T][0] + 1) res += cnt[T][1];
    return res;
}

int main()
{
    int cases;
    scanf("%d", &cases);
    while(cases --){
        scanf("%d%d", &n, &m);
        memset(h, -1, sizeof h);
        while(m --){
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, c);
        }
        scanf("%d%d", &S, &T);
        printf("%d\n", dijkstra());
    }
    return 0;
}
posted @   小菜珠的成长之路  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示