图论-Dijkstra算法

//使用邻接矩阵并记录路径的Dijkstra
#include<vector> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int MAXV = 1000; const int INF = 0xFFFFFFF; int n,m,G[MAXV][MAXV]; int d[MAXV]; int pre[MAXV]; bool vis[MAXV]={false}; void Dijkstra(int s) { for(int i=0;i<n;i++) d[i]=INF,pre[i]=i; //fill(d,d+MAXV,INF); d[s]=0; for(int i=0;i<n;i++)//必须把每个定点标记为访问过 { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; //说明无法到达其他的定点,也就是其他的点到s的距离为d[i]=INF,此时直接返回即可 vis[u]=true; //访问这一点 for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=0 && d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; pre[v]=u; } } } } void print(int u) { if(pre[u]!=u) print(pre[u]); printf("%d ",u); } int main(void) { int s,u,v,w; cin>>n>>m>>s; for(int i=0;i<m;i++) { cin >> u >> v >> w; G[u][v]=w; } Dijkstra(s); for(int i=0;i<n;i++) printf("%d ",d[i]); printf(" "); for(int i=0;i<n;i++) { print(i); printf(" "); } return 0; }

测试数据:

6 8 0
 0 1 1 
 0 3 4 
 0 4 4 
 1 3 2 
 2 5 1 
 3 2 2 
 3 4 3 
 4 5 3
第一行是n,m,s;
然后是m行起点,终点,权重
//使用邻接表的Dijkstra,输出路径
#include<cstdio> #include<iostream> #include<vector> #include<algorithm> using namespace std; const int MAXV = 1000; const int INF = 0xFFFFFFF; int n,pre[MAXV],d[MAXV],vis[MAXV]; struct Node{ int v,dis; Node(int _v,int _dis){ v=_v; dis=_dis; } }; vector<Node>Adj[MAXV]; void print(int u) { if(pre[u]!=u) print(pre[u]); printf("%d ",u); } void Dijkstra(int s) { for(int i=0;i<n;i++) d[i]=INF,pre[i]=i; d[s]=0; for(int i=0;i<n;i++) { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; vis[u]=true; for(int j=0;j<Adj[u].size();j++) { int v=Adj[u][j].v; if(vis[v]==false && d[u]+Adj[u][j].dis<d[v]) { pre[v]=u; d[v]=d[u]+Adj[u][j].dis; } } } } int main(void) { int u,v,w,m,s; cin>>n>>m>>s; for(int i=0;i<m;i++) { cin >> u >> v >> w; Adj[u].push_back(Node(v,w)); } Dijkstra(s); for(int i=0;i<n;i++) printf("%d ",d[i]); printf(" "); for(int i=0;i<n;i++) { printf("路径:%d:",i+1); print(i); printf(" "); } return 0; }

 输出路径的函数使用如下更加方便:

void DFS(int s,int v)
{
    if(v==s)
    {
        printf("%d
",v);
        return ;    
    }    
    DFS(S,pre[v]);
    printf("%d
",v);
} 

 Dijkstra算法在有两条或者两条以上的最短路径可以到达同一个地方的时候,一般会给出第二个标尺。

1.边的权值

2.点的权值

3.最短的路径的个数

//边权的使用,邻接矩阵的情况,注意初始化只有c[v]=0,其他为INF
//对于邻接表,可以使用邻接表存放一个顶点可以到达的其他点,然后将边权仍然放到一个矩阵中,方便查询
for
(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; c[v]=c[u]+cost[u][v]; } else if(d[u]+G[u][v]==d[v] && c[u]+cost[u][v]<c[v]) { c[v]=c[u]+cost[u][v]; } } }
//增加点权的情况,初始化时候只有w[s]=weight[s],其他为INF
for
(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; w[v]=w[u]+weight[v]; } else if(d[u]+G[u][v]==d[v] && w[v]<w[u]+weight[v]) { w[v]=w[u]+weight[v] } } }
//计算路径:初始化只有num[s]=1,其他为INF;
for
(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; num[v]=num[u]; } else if(d[u]+G[u][v]==d[v]) { num[v]+=num[u]; } } }

举例子:

给出N个城市,M条无向边。每个城市都有一定的救援小组,所有边的权值已知(此处的边权不是路径长度,是第二尺度)。给出起点和终点,求从起点到终点的最短路径条数以及路径上的救援小组之和。如果有多条最短路径,输出救援小组数目之和最大的。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXV = 1000;
const int INF = 0xFFFFFFF;
//n为定点数,m为边数,st起点,ed终点
//G为邻接矩阵,weight为点权
//d[]记录最短路径长度,w[]记录最大点权之和,num[]记录最短路径数量
int n,m,st,ed,G[MAXV][MAXV],weight[MAXV];
int d[MAXV],w[MAXV],num[MAXV];
bool vis[MAXV]={false};
void Dijkstra(int s)
{
    fill(d,d+MAXV,INF);
    memset(num,0,sizeof(num));    
    memset(w,0,sizeof(w));
    d[s]=0;
    w[s]=weight[s];
    num[s]=1;
    for(int i=0;i<n;i++)
    {
        int u=-1,MIN=INF;
        for(int j=0;j<n;j++)
        {
            if(vis[j]==false && d[j]<MIN)
            {
                u=j;
                MIN=d[j];
            }
        }
        if(u==-1) return ;
        vis[u]=true;
        for(int v=0;v<n;v++)
        {
            if(vis[v]==false && G[u][v]!=INF)
            {
                if(d[u]+G[u][v]<d[v])
                {
                    d[v]=d[u]+G[u][v];
                    w[v]=w[u]+weight[v];
                    num[v]=num[u];    
                }    
                else if(d[u]+G[u][v]==d[v])
                {
                    if(w[u]+weight[v]>w[v]) w[v]=w[u]+weight[v];
                    num[v]+=num[u]; //写在if的外面 
                }
            }    
        } 
    }
}
int main(void)
{
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    for(int i=0;i<n;i++) scanf("%d",&weight[i]);
    int u,v;
    fill(G[0],G[0]+MAXV*MAXV,INF);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&u,&v);
        scanf("%d",&G[u][v]);
        G[v][u]=G[u][v];    
    }    
    Dijkstra(st);
    //for(int i=0;i<n;i++) printf("%d  %d  %d
",d[i],w[i],num[i]); 
    printf("%d %d
",num[ed],w[ed]);
    return 0;
} 
/*
5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
答案:2 4  
*/

 Dijkstra+DFS

//Dijkstra部分
void
Dijkstra(int s) { fill(d,d+MAXV,INF); d[s]=0; for(int i=0;i<n;i++)//必须把每个定点标记为访问过 { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; //说明无法到达其他的定点,也就是其他的点到s的距离为d[i]=INF,此时直接返回即可 vis[u]=true; //访问这一点 for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; pre[v].clear(); pre[v].push_back(u); } else if(d[u]+G[u][v]==d[v]) { pre[v].push_bakc(u); } } } } }
//DFS部分
int
optvalue; vector<int>pre[MAXV]; vector<int>path,tempPath; void DFS(int v) { if(v==st) { tempPath.push_back(v); int value; 计算路径tempPath上的value的值:累加边权或者点权的和 if(value 优于 optvalue) { optvalue=value; path=tempPath; } tempPath.pop_back(); } tempPath.push_back(v); for(int i=0;i<pre[v].size();i++) { DFS(pre[u][i]); } tempPath.pop_back(v); }

总结:裸的迪杰斯特拉使用邻接表计算,带有第二尺度的使用Dijkstra的邻接矩阵的方法计算,复杂的第二尺度,使用Dijkstra+DFS计算。

原文地址:https://www.cnblogs.com/zuimeiyujianni/p/8836706.html