【BZOJ2750】【HAOI2012】道路(最短路+拓扑)

本文探讨了在有向图中使用最短路径算法来计算每条边的贡献次数,即通过该边的最短路径数量。首先通过枚举起点进行最短路径搜索,再利用拓扑排序思想递推计算每个节点的最短路径方案数,最后统计经过每条边的最短路径数量。文章详细介绍了Dijkstra算法的应用,以及如何通过两次深度优先搜索(DFS)分别计算最短路径方案数和最短路径数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

容易想到枚举所有起点 做最短路 然后枚举边统计次数

一条边(x,y)的贡献 肯定是 s到x最短路的方案数 乘上 s到其他点但经过了y的最短路

对于前者

每个点可以从前一个点递推过来 只要满足dis[vis]==dis[now]+edge[u].val 当一个点被所有入边都统计了一次后 就可以搜他了(拓扑思想)

对于后者

每个点从后一个点递推过来

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define N 5505
#define M 5005
#define ll long long
using namespace std;
const int mod=1000000007; 
template <class T>
inline void read(T &x)
{
    x=0;
    static char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
}
int n,m,first[N],tot,dis[N];
ll a[N],b[N],ans[N];
struct Edge
{
    int from,to,next,val;
}edge[M];
inline void addedge(int x,int y,int z)
{
    tot++;
    edge[tot].from=x; edge[tot].to=y; edge[tot].next=first[x]; edge[tot].val=z; first[x]=tot;
}
bool visit[N],onroad[N];
typedef pair<int,int> Pair;
void Dijkstra(int s)
{
    priority_queue<Pair,vector<Pair>,greater<Pair> > heap;
    memset(dis,0x3f,sizeof(dis)); memset(visit,false,sizeof(visit));
    heap.push(make_pair(0,s)); dis[s]=0;
    while(!heap.empty())
    {
        int now=heap.top().second;
        heap.pop();
        if(visit[now])  continue;
        visit[now]=true;
        for(int u=first[now];u;u=edge[u].next)
        {
            int v=edge[u].to;
            if(dis[now]+edge[u].val<dis[v])
            {
                dis[v]=dis[now]+edge[u].val;
                heap.push(make_pair(dis[v],v));
            }
        }
    }
}
int in[N];
void dfs1(int now)
{
    visit[now]=true;
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(dis[vis]==dis[now]+edge[u].val)  //说明在最短路上
        {
            in[vis]++;
            if(visit[vis])  continue;
            dfs1(vis);
        } 
    }
}
void dfs2(int now)
{
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(dis[vis]==dis[now]+edge[u].val)
        {
            onroad[u]=true;
            a[vis]=(a[vis]+a[now])%mod;
            in[vis]--;
            if(in[vis]==0)  dfs2(vis); 
        }
    }
}
void dfs3(int now)
{
    b[now]=1;
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(dis[now]+edge[u].val==dis[vis])
        {
            if(!b[vis]) dfs3(vis);  //最短路没有环 放心dfs 
            b[now]=(b[now]+b[vis])%mod;
        }
    }
}
void Init()
{
    memset(onroad,false,sizeof(onroad));
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(in,0,sizeof(in));
}
int main()
{
    read(n),read(m);
    for(int i=1,x,y,z;i<=m;i++)
    {
        read(x),read(y),read(z);
        addedge(x,y,z);
    }
    for(int i=1;i<=n;i++)
    {
        Init();
        Dijkstra(i);
        memset(visit,0,sizeof(visit));
        dfs1(i);    //来自最短路上的入度
        a[i]=1;
        dfs2(i);    //i到每个点最短路的方案数
        dfs3(i);    //i制造的最短路经过每个点的数量 
        for(int j=1;j<=m;j++)   if(onroad[j])   ans[j]=(ans[j]+a[edge[j].from]*b[edge[j].to]%mod)%mod;
    }
    for(int i=1;i<=m;i++)   cout<<ans[i]<<'\n';
    return 0;
}

转载于:https://www.cnblogs.com/Patrickpwq/p/9889833.html

vb+mapinfo 实现短路 'NoNode1 起始点编码 'NoNode2 结束点编码 'nNode 为网中大的节点数 'NoNode(i) 节点编码 'LinkN(i) 与i点相连Line个数 'LinkNi(i) 与i点相连Line端点存放序号 'iLL=LinkNi(LL) - i + 1 'LinkNo(iLL) 与i点相连Line端点顺序编号 'LinkList(iLL) 与i点相连Line端点距离 'nNodeShortPath 短路径节点数 'NodeShortPath 短路径节点序号 Public Sub ShortPathSearch(NoNode1 As Integer, NoNode2 As Integer, nNode As Integer, NoNode() As Integer, LinkN() As Integer, LinkNi() As Integer, LinkNo() As Integer, LinkDis() As Double, nNodeShortPath As Integer, NodeShortPath() As Integer, ShortPath As Double) Dim II As Integer, I As Integer, J As Integer, LL As Integer, iLL As Integer Dim iNode As Integer Dim S As Single, MinS As Single, MinPoint As Integer Dim NodeCheck() As Boolean '标记已经查过的点 Dim NodeUse() As Boolean '标记已经作为结果点使用过的点 Dim RS() As Single '假设从起点到任一点的距离都为无穷大 Dim result() As Single '存放结果长度 Dim ResultNo() As Integer '存放结果节点编号 Dim iResult As Integer Dim No() As Integer Dim StartNo As Integer, EndNo As Integer 'Begin根据编码,搜索序号 StartNo = 0 EndNo = 0 For I = 1 To nNode If (NoNode(I) = NoNode1) Then StartNo = I End If If (NoNode(I) = NoNode2) Then EndNo = I End If If (StartNo > 0 And EndNo > 0) Then Exit For Next I 'End根据编码,搜索序号 ReDim NodeCheck(1 To nNode), NodeUse(1 To nNode) ReDim RS(1 To nNode), result(1 To nNode), ResultNo(1 To nNode) For I = 1 To nNode NodeCheck(I) = False '标记未经查过的点。 NodeUse(I) = False '标记未作为结果点使用过的点 RS(I) = 1E+38 '假设从起点到任一点的距离都为无穷大 Next I LL = StartNo '设置开始点。 NodeUse(LL) = True '标记开始点为真。即已经作为结果点使用过。 J = 0 For iNode = 1 To nNode '先从与开始点相连的起点寻找 For I = 1 To LinkN(LL) '以与LL点相连的起点的个数循环 iLL = LinkNi(LL) + I - 1 iResult = LinkNo(iLL) '找出与LL点相连的起点的点号 If NodeCheck(iResult) = False Then '如果没查过,则进行 S = LinkDis(iLL) + result(LL) '找出长度并求和 If NodeUse(iResult) = True Then '如果已经作为结果点,判断哪一个长 If S <= RS(iResult) Then '如果这一点到起点的长度比现在的路线长,替代 RS(iResult) = S result(iResult) = S '设置到这点的短路径长度 ResultNo(iResult) = LL End If Else '如果上面的条件都不符合,则进行下面的语句 NodeCheck(iResult) = True RS(iResult) = S result(iResult) = S ResultNo(iResult) = LL J = J + 1 '每找到一个点加一,为了下面的判断 ReDim Preserve No(1 To J) '重新定义数组并使其值为当前的点号 No(J) = iResult End If End If Next I '设置小为无穷大,短路径点为空 MinS = 1E+38 MinPoint = 0 '找出已经查过点中长度短的点 For I = iNode To J If RS(No(I)) < MinS Then II = I MinS = RS(No(I)) MinPoint = No(I) End If Next I '如果没有结果,即起点与终点没有通路,则退出程序 If MinS = 1E+38 Then MsgBox "即起点与终点没有通路!", vbOKOnly, "关于搜索短路径" Exit Sub End If '将两点互换,减少循环。 No(II) = No(iNode) No(iNode) = MinPoint '标记已经作为结果点判断过 NodeUse(MinPoint) = True LL = MinPoint '判断结果点是否等于终点,如果等于则已经找到短路径 If MinPoint = EndNo Then Exit For Next iNode 'Begin检索短路径节点 ReDim NodeShortPath(1 To nNode) nNodeShortPath = 1 LL = MinPoint NodeShortPath(nNodeShortPath) = LL Do LL = ResultNo(LL) nNodeShortPath = nNodeShortPath + 1 NodeShortPath(nNodeShortPath) = LL If (LL = StartNo) Then Exit Do Loop 'End检索短路径节点 ShortPath = result(EndNo) End Sub
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值