图论--最短路

dijkstra

/*将顶点分为两个集合:已求得最短距离的集合1,待求集合2。
(1) 在集合2中找一个到start距离最近的顶点k : min{d[k]}
(2) 把顶点k加到集合1中,同时检查集合2中的剩余顶点j的d[j]是否
经过k后变短,如果变短修改d[j]。
if ( d[k]+a[k][j]<d[j] ) d[j]=d[k]+a[k][j]
(3) 重复(1)和(2),直至集合2空为止。*/

//不能处理负边,也不能处理负权回路 

#include<iostream>
#include<string.h>
#define TNF 2147483647
using namespace std; 
long long int dis[500005];//dis表示两个点之间的距离
long long int book[500005];//book表示该点是否被打了标记 
long long int value[500005],to[500005];//value为各个边的长度;to为各边连接的点
long long int head[500005] ;
long long int next[500005];//next函数表示该条直线的初始点连接的下一条点 
long long int m,n,s,total;//totle为总的个数;//m 为点的个数;n为边的个数;s为出发点 
void add(long int a,long int b,long int c)//a为起点,b为终点,c为长度/*目的是插入这个点和连接的点以及两点之间的长度 
{
    total++;
    to[total]=b;
    value[total]=c;
    next[total]=head[a];
    head[a]=total;          
} 
void dijkstra(int u)//计算长度  u为出发点; 
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=TNF;
    }//初始化,附一个极大的值
    memset(book,0,sizeof(book));//给标记初始化,所有的点都被标记为0;后来打标记为1加以区分
    dis[u]=0;//按题目中所说改电到出发点的距离都是本身即为0
    for(int i=1;i<=n;i++)//全都扫一遍
    {
        int start=-1;//点的标记是从0开始,不存在-1的点
        for(int j=1;j<=n;j++)
        {
            if(book[j]==0&&(dis[j]<dis[start]||start==-1))start=j;
        } 
        //这样不断的for一定可以找出一个距离已经进队的元素最近的一个 
        book[start]=1;//start这个元素进队,然后更新以下没有进队的元素与已经进队的元素的最近距离,如果可以更新的话 
            for(int e=head[start];e;e=next[e])//更新 
            {
                dis[to[e]]=min(dis[to[e]],dis[start]+value[e]);
            }
    } 
}
int main()
{
    cin>>n>>m>>s;
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    dijkstra(s);
    for(int i=1;i<=n;i++)
    {
        cout<<dis[i]<<" ";  
    }

} 

Floyd

//可以处理负边,但是无法处理负权回路 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#define maxn 0x7f7f
using namespace std;
int n,m,s;
int dis[10000][10002];
void floyd()
{
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if((dis[i][k]+dis[k][j])<dis[i][j])
                dis[i][j]=dis[i][k]+dis[k][j];
            }
        }
    }
}
int main()
{
    cin>>n>>m>>s;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)
            {
                dis[i][j]=0;    
            }
            else{
                dis[i][j]=maxn;
            }
        }
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        dis[x][y]=z;
        dis[y][x]=z;     
    } 
    floyd();
    cout<<dis[1][n]<<" ";
 } 
 /*
 floyd算法可以求任意两点之间的最短路 
 */

spfa +矩阵

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#define INF 9999999
using namespace std;
int a[1000][1000];//用邻接矩阵来存储图 
int q[10001],dis[10001],b[201][201];
bool vis[201];
int m,n,s,t;
void spfa(int s)
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=INF;
    }
    dis[s]=0;vis[s]=1;q[1]=s;
    int head=0,tail=1;
    while(head<=tail)//队列不为空就可以一直进行操作 
    {
        head++;//this line &next line 取出队首的操作 
        int v=q[head];//队首
        vis[v]=0; //取出队首以后一定要释放标记,保证可以重复进队
        for(int i=1;i<=b[v][0];i++)
        //取出当前节点要用当前的节点去更新和他相连的所有节点
//      如果有节点没有进队,加入队列中 
        {
            if(dis[b[v][i]]>dis[v]+a[v][b[v][i]])
            {
                dis[b[v][i]]=dis[v]+a[v][b[v][i]];
                if(vis[b[v][i]]==0)
                {
                    tail++;
                    q[tail]=b[v][i];
                    vis[b[v][i]]=1;
                }
            }
         } 
    }
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        if(a[x][y]!=0&&z>a[x][y])//若x,y之间不只有一条,保留最小的
        continue;
        //对于next line的解释,以x为节点的变数++,并且以x为其中的 一个点
//       ,第 b[x][0]条边所连接的另一个点是y,并且这两个点之间的权值为z 
        b[x][0]++;b[x][b[x][0]]=y;a[x][y]=z;
        //b[x][0],以x为一个节点的边的条数
        b[y][0]++,b[y][b[y][0]]=x;a[y][x]=z;//双向建边      
    }
    cin>>s>>t;//输入起点和终点
    spfa(s);
    if(dis[t]!=INF)cout<<dis[t]<<endl;
    else{
        cout<<-1<<endl;
    } 
    return 0;
}

各种最短路算法比较 1.dijkstra : 不能处理负边,不能处理负权回路,侧重对点的处理,适用于稠密图(和顶点之类的进行联系)时间复杂度,(m+nlogn)空间复杂度,m 2.floyd :侧重于对点的处理,可以处理负权边,但是不能处理负权回路,适用于稠密图,空间复杂度n^2,时间复杂度,n^3,只有数据规模较小且时空复杂度都允许时才可以使用 3.bellman-ford :可以处理负权边,但是不能处理负权回路,适用于稀疏图,时间复杂度m,空间复杂度(mn),侧重于对边的处理 4.spfa实际上就是队列优化的bellman-ford,空间复杂度M,时间复杂度NM,侧重于对边的处理 适用于稀疏图,可以解决负权边和负权回路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值