dijkstra 复杂度O(n2)
基本思想:最短路:dijkstra算法+路径输出
建边写法:建边——邻接矩阵、vector、链式向前星
- 邻接矩阵和vector的版本:
直接贴学长博客了:图论基本知识 - 邻接表(链式向前星):
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=1e5+10;
const int INF=0x3f3f3f3f;
int dis[300],vs[300],head[300];
int n,m,cou;
struct node
{
int e,len,bf;//边的终点、边长、和这个点有相同起点的上一条边
} edge[manx<<2];
void add(int s,int e,int len)
{
edge[cou]=node{e,len,head[s]};
head[s]=cou++;
}
void dij(int s,int e)
{
memset(dis,INF,sizeof(dis));
memset(vs,0,sizeof(vs));
vs[s]=1;
dis[s]=0;
int nx=s;
while(!vs[e])
{
for(int i=head[nx]; ~i; i=edge[i].bf)
{
int nlen=edge[i].len;
int ne=edge[i].e;
if(dis[ne]>dis[nx]+nlen)
{
dis[ne]=dis[nx]+nlen;
}
}
nx=-1;//标记距离最小的位置
for(int j=1; j<=n; j++)
{
if(!vs[j])//j点没有走过
if(nx==-1||dis[j]<dis[nx])
nx=j;
}
if(nx==-1)
break;
vs[nx]=1;
}
}
int main()
{
int sx,ex,len,s,e;
while(scanf("%d%d",&n,&m),n||m)
{
cou=1;
memset(head,-1,sizeof(head));
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&sx,&ex,&len);
add(sx,ex,len);
add(ex,sx,len);
}
//scanf("%d%d",&s,&e);
s=1,e=n;
dij(s,e);
if(dis[e]==INF)
{
if(s==e)
printf("0\n");
else
printf("-1\n");
}
else
printf("%d\n",dis[e]);
}
}
1、可以思考下这3种写法,分别是怎么遍历完有相同起点的所有边的
2、处理完后,没有路径的顶点的结果是什么
3、为什么dij不能解决边权有负值的情况
优先队列优化dij+链式前向星 复杂度O(mlogn)
m是图的边数,n是顶点数
用优先队列优化后,就可以直接从队首得到下一个顶点,省去了遍历图中每个顶点去找下一个顶点(如果此时与处在队首的顶点已经遍历过了,即vs[pos]=1,因为某个节点可能被放进去很多次,后面的就可以直接跳过)
定义一个结构体,结构体中包含两个数据:顶点和该顶点到起点的距离,然后更新一次就将这个顶点和新的距离值放入队列,设置下结构体的优先级,让距离大的沉到队尾
代码(没有提交过,但是大概写法是这样子得):
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
#define chl (root<<1)
#define chr ((root<<1)|1)
#define mid ((l+r)>>1)
const int manx=1e5+10;
const int INF=0x3f3f3f3f;
int cou=0,head[1010],path[manx][2],dis[1010];
bool vs[1010];
struct node
{
int e,len,bf;
} edge[manx];
void add(int s,int e,int len)
{
edge[cou]=node{e,len,head[s]};
head[s]=cou++;
}
struct poi
{
int pos,len;
};
bool operator<(poi a,poi b)
{
return a.len>b.len;
}
int dij(int s,int e)
{
priority_queue<poi>qu;
memset(dis,INF,sizeof(dis));
memset(vs,0,sizeof(vs));
//vs[s]=1;
dis[s]=0;
int nx=s;
qu.push(poi{s,0});
while(!qu.empty())
{
nx=qu.top().pos;
qu.pop();
//if(vs[nx])
// continue;
vs[nx]=1;
if(vs[e])
break;
for(int i=head[nx]; ~i; i=edge[i].bf)
{
int nlen=edge[i].len;
int ne=edge[i].e;
if(dis[ne]>dis[nx]+nlen)
{
dis[ne]=dis[nx]+nlen;
qu.push(poi{ne,dis[ne]});
}
}
}
return dis[e];
}
int main()
{
int n,m,s,e,ans,a,b,len;
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&s,&e);
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&a,&b,&len);
path[i][0]=a;
path[i][1]=b;
add(a,b,len);
add(b,a,len);
}
ans=dij(s,e);
printf("%d\n",ans);
}
/*
5 5 1 5
1 2 2
2 3 2
2 4 2
3 5 100
4 5 2*/
SPFA 期望复杂度O(kE)
E是图的边数,k是一个常数很多情况下k不超过2(不包含有可到达负环的情况)
可以解决边权有负值的,最长路等等dij无法处理的。
扫描二维码关注公众号,回复:
8612614 查看本文章
链式向前星版本:
#include <iostream>
#include <string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=1e5+10;
const int INF=0x3f3f3f3f;
int dis[300],vs[300],head[300];
int n,m,cou;
struct node
{
int e,len,bf;//边的终点、边长、和这个点有相同起点的上一条边
}edge[manx<<2];
void add(int s,int e,int len)
{
edge[cou]=node{e,len,head[s]};
head[s]=cou++;
}
vector<node>vt[300];
int spfa(int s)
{
int c[300];
queue<int>qu;
memset(c,0,sizeof(c));
memset(dis,INF,sizeof(dis));
memset(vs,0,sizeof(vs));
dis[s]=0;
vs[s]=1;
c[s]=1;
//c数组判断是否有负环(并且有从源点到负环的路径),即c[s]是否大于n
qu.push(s);
int sx;
while(!qu.empty())
{
sx=qu.front();//边的起点
qu.pop();
for(int i=head[sx];i;i=edge[i].bf)//i表示边的序号
{
int nx=edge[i].e;
int nlen=edge[i].len;
if(dis[sx]+nlen<dis[nx])
{
dis[nx]=dis[sx]+nlen;
if(!vs[nx])
{
qu.push(nx);
vs[nx]=1;
c[nx]++;//nx被放入队列的次数
if(c[nx]>n)
return 0;
}
}
}
vs[sx]=0;
}
return 1;
}
int main()
{
int sx,ex,len,s,e;
while(scanf("%d%d",&n,&m),n||m)
{
cou=1;
memset(head,0,sizeof(head));
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&sx,&ex,&len);
add(sx,ex,len);
add(ex,sx,len);
}
//scanf("%d%d",&s,&e);
s=1,e=n;
int flag=spfa(s);
if(!flag)
continue;
if(dis[e]==INF)
{
if(s==e)
printf("0\n");
else
printf("-1\n");
}
else
printf("%d\n",dis[e]);
}
}