【洛谷题解/AcWing题解/BAPC2006资格赛】洛谷P10947 Sightseeing/Acwing 383 观光

题目链接

洛谷: https://www.luogu.com.cn/problem/P10947
AcWing: https://www.acwing.com/problem/content/description/385/

关联知识

单源最短路与Dijkstra算法

思路分析

本题旨在要求我们求出最短路次最短路(且总距离仅比最短路小于1) 的路径数量综合。对于本题目,笔者建议毫无头绪的同学可以先尝试这一题:最短路计数。本文的题目实际上是对该题的扩展延伸。

最短路计数要求我们统计最短路的条数,在这题中,我们采用了分类讨论的方式,想到在更新最短路的同时也更新最短路的条数。用数组 c n t cnt cnt 记录到达当前点的最短路的条数, d i s t a n c e distance distance 为到达当前点的最短路, w w w 为当前点到领接点的边权,如果最短路可以被更新,那么 c n t cnt cnt 数组则被更新成新路径所记录的条数;如果不满足上面一种情况,但是当前领接点所记录的最短路恰好等于 d i s t a n c e + w distance+w distance+w,那么说明这是一条新出现的最短路路径, 将两条路径的 c n t cnt cnt 相加即可。

而在观光一题中,题目要求多了一个——求距离比最短路小 1 的次最短路的条数。我们可以从最短路计数一题中得到启发,依然采用分类讨论的方式。为了实时记录最短路和次最短路两个数据,我们可以在经典最短路算法的数组的基础上扩展为二维数组,用 0 表示最短路,用 1 表示次最短路(见代码)

  • 情况一:当前记录最短路 > d i s t a n c e + w >distance+w >distance+w
    这是优先级最高的情况,如果当前记录的最短路可以被更新,那么当前记录的最短路距离则可以变成次最短路。同时,令次最短路的条数等于最短路的条数,将最短路条数同步进行更新。
  • 情况二:当前记录最短路 = d i s t a n c e + w =distance+w =distance+w
    如果不满足情况一,但是满足此情况,则说明出现了一条新的可以走出最短路的路径,将两种路径的 c n t cnt cnt 进行合并即可。
  • 情况三:当前记录次最短路 > d i s t a n c e + w >distance+w >distance+w
    如果不满足情况一、二,但是满足此情况,则说明 d i s t a n c e + w distance+w distance+w 虽然不足以成为最短路,但是可以作为次最短路。则将次最短路的数据类比情况一进行更新。
  • 情况四:当前记录次最短路 = d i s t a n c e + w =distance+w =distance+w
    如果前三种情况都不满足,但是 d i s t a n c e + w distance+w distance+w 恰好等于当前记录的次最短路,则说明出现了一条新的可以走出次最短路的路径,将两种路径的 c n t cnt cnt 类比情况二进行合并即可。

如果一种情况都不满足,说明对最短路和次最短路不构成影响,不用管。

AC代码

细节点提示:
1.数据有多组,该重置的数组、数据一定要记得重置;
2.分类情况较多,不要混淆最短路和次最短路,更不要弄错数据;
3.本题的次最短路必须满足比最短路距离小 1,一定要做特判。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

const int N = 1100, M = 1e4 + 10,  INF = 0x3f3f3f3f;

struct Edge
{
    int u, type, dist;
    bool operator> (const Edge &a) const
    {
        return dist > a.dist;
    }
};
int T, n, m, s, f;
int st[N][2], dis[N][2], cnt[N][2];
int ne[M], h[N], e[M], w[M], idx;

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

int dijkstra()
{
    memset(st, 0, sizeof st);
    memset(dis, INF, sizeof dis);
    memset(cnt, 0, sizeof cnt);
    //类型0为最短路,类型1为次短路
    dis[s][0] = 0;
    cnt[s][0] = 1;
    priority_queue<Edge, vector<Edge>, greater<Edge>> q;
    q.push({s, 0, 0});
    
    while (!q.empty())
    {
        auto now = q.top();
        q.pop();
        int nowu = now.u, nowtype = now.type, nowdis = now.dist;
        if (st[nowu][nowtype]) continue;
        st[nowu][nowtype] = true;
        
        for (int i = h[nowu]; i != -1; i = ne[i])
        {
            int j = e[i];
            //当前值可以更新最小值
            if (dis[j][0] > nowdis + w[i])
            {
                cnt[j][1] = cnt[j][0];
                dis[j][1] = dis[j][0];
                q.push({j, 1, dis[j][1]});
                dis[j][0] = nowdis + w[i];
                cnt[j][0] = cnt[nowu][nowtype];
                q.push({j, 0, dis[j][0]});
            }
            else if (dis[j][0] == nowdis + w[i])
            {
                //当前值是一条新路线的等距离的最短路
                cnt[j][0] += cnt[nowu][nowtype];
            }
            else if (dis[j][1] > nowdis + w[i])
            {
                //当前值虽然不足以成为最短路,但是可以成为次短路
                dis[j][1] = nowdis + w[i];
                cnt[j][1] = cnt[nowu][nowtype];
                q.push({j, 1, dis[j][1]});
            }
            else if (dis[j][1] == nowdis + w[i])
            {
                cnt[j][1] += cnt[nowu][nowtype];
            }
        }
    }
    
    int res = cnt[f][0];
    if (dis[f][1] == dis[f][0] + 1) res += cnt[f][1];
    return res;
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        memset(h, -1, sizeof h);
        idx = 0;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, c);
        }
        scanf("%d%d", &s, &f);
        int ans = dijkstra();
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oier_Asad.Chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值