您的位置:首页 > 其它

[模板]-单源最短路径

2017-08-19 11:22 393 查看
问题描述:

给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

代码①:邻接矩阵 + 裸迪杰斯特拉

#define inf 0x3f3f3f3f//0x7fffffff
int mapp[1001][1001],book[1001],dis[1001],n;//标记数组book,距离数组dis,顶点数n
void dijkstra(int x)
{
int Min,p;
for(int i = 1; i <= n; i++)//初始化源点x到其余各个顶点的路程
dis[i] = mapp[x][i];
dis[x] = 0;
book[x] = 1;//标记
for(int i = 1; i <= n - 1; i++)//只需要松弛n - 1轮
{
Min = inf;//初始化最小值
for(int j = 1; j <= n; j++)//从各个顶点中找出
if(book[j] == 0 && Min > dis[j])没被标记过且距离源点最近的
{
//更新
p = j;
Min = dis[j];
}
book[p] = 1;//找到了以后,标记该点
for(int j = 1; j <= n; j++)//以p点为中转点,进行松弛操作
if(book[j] == 0 && Min + mapp[p][j] < dis[j])//必须没被标记过
dis[j] = Min + mapp[p][j];//更新
}
for(int i = 1; i <= n; i++)//按照题意输出
if(dis[i] == inf)
printf("2147483647 ");
else
printf("%d ",dis[i]);
printf("\n");
}
int main()
{
int m,s,f,g,w;
scanf("%d%d%d",&n,&m,&s);//n个顶点,m条边,查询s点
//初始化邻接矩阵
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(i == j)
mapp[i][j] = 0;
else
mapp[i][j] = inf;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d",&f,&g,&w);//点f到点g权值为w
if(f == g)//特判
continue;//防止数据有坑
if(w >= mapp[f][g])//不能使较大值将较小值覆盖
co
f573
ntinue;
mapp[f][g] = w;//没问题,放心赋值
}
dijkstra(s);//调用
return 0;
}


算法的基本思想是:每次找到离源点最近的一个顶点,然后以该顶点为中转点进行扩展,最终得到源点到其余所有点的最短路径

代码②:邻接链表 + SPFA(贝尔曼-福德算法的队列优化)



#define inf 0x3f3f3f3f//0x7fffffff
int book[10001],dis[10001],n;//标记数组book,距离数组dis,顶点数n
struct Node//结构体
{
int v;//顶点编号
int w;//权值
struct Node* next;//后继指针
};
Node Head[10001];//最多1万个顶点
queue<int> que;//队列
void CreatGraph(int node1,int node2,int w)//顶点node1到顶点node2,权值为w
{
Node* pointer;//定义指针
Node* New;//定义指针
New = (Node*)malloc(sizeof(struct Node));//生成新结点

if(New != NULL)//如果成功
{
New -> v = node2;//赋顶点编号
New -> w = w;//赋权值
New -> next = NULL;//将后继指针置为空
pointer = &(Head[node1]);//指向顶点node1
while(pointer -> next != NULL)
pointer = pointer -> next;
pointer -> next = New;//将node2与node1建立关系
}
}
void spfa(int x)
{
Node* pointer;//定义指针
que.push(x);//将源点入队
book[x] = 1;//标记源点已经入队
//初始化
for(int i = 1; i <= n; i++)
dis[i] = inf;
dis[x] = 0;
while(!que.empty())//当队列不为空时进行循环
{
int i = que.front();
pointer = &(Head[i]);//指向当前需要处理的队首顶点
while(pointer != NULL)//扫描当前顶点所有的边
{
//     顶点x到顶点v   顶点x到顶点i   顶点i到顶点v的权值
if(dis[pointer -> v] > dis[i] + pointer -> w)//判断是否松弛成功
{
dis[pointer -> v] = dis[i] + pointer -> w;//更新
if(book[pointer -> v] == 0);//如果顶点v不在队列中
{
que.push(pointer -> v);//入队
book[pointer -> v] = 1;//标记
}
}
pointer = pointer -> next;
}
book[i] = 0;//取消标记
que.pop();//出队
}
for(int i = 1; i <= n; i++)//按照题意输出
if(dis[i] == inf)
printf("2147483647");
else
printf("%d ",dis[i]);
printf("\n");
}
int main()
{
int m,s,f,g,w;
scanf("%d%d%d",&n,&m,&s);//n个顶点,m条边,查询s点
for(int i = 1; i <= n; i++)//初始化邻接链表
{
Head[i].v = i;//顶点的编号
Head[i].w = 0;//权值赋为0
Head[i].next = NULL;//后继指针赋为空
}
Node* pointer;//定义一个指针
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d",&f,&g,&w);//点f到点g权值为w
if(f == g)//特判
continue;//防止数据有坑
pointer = &(Head[f]);//将指针指向顶点f
//注意必须将pointer != NULL放在前面,防止发生未定义行为
while(pointer != NULL && pointer -> v != g)//查询是否已经与顶点g建立了关系
pointer = pointer -> next;
if(pointer == NULL)//如果没有
CreatGraph(f,g,w);
else if(pointer -> v == g && pointer -> w > w)//如果已经与顶点g建立了关系且输入的w比以往的w还小
pointer -> w = w;//就更新一下w
else
continue;
}
spfa(s);//调用
return 0;
}


算法的关键之处在于:只有那些在前一遍松弛中改变了最短路程估计值的顶点,才可能引起它们邻接点最短路程估计值发生改变

代码③:链式前向星 + 堆优化迪杰斯特拉:

对链式前向星不了解的朋友请看:http://blog.csdn.net/suntengnb/article/details/77450558

typedef pair<int,int> Pair;//pair类型的使用比较繁琐,所以用typedef简化声明
priority_queue<Pair,vector<Pair>,greater<Pair> > pq;//小顶堆
struct Edge
{
int next;//edge[i].next表示与第i条边同起点的下一条边的存储位置
int to;//edge[i].to表示第i条边的终点
int w;//权值
};
Edge edge[500010];//注意要存成边的上限
int cnt = 1, head[10001],dis[10001],book[10001];//cnt从1开始,head[i]表示以i为起点的第一条边存储的位置,dis[i]表示源点到顶点i的最短距离,book数组用来标记
void AddEdge(int f,int g,int w)//增加一条边
{
edge[cnt].next = head[f];
edge[cnt].to = g;//终点
edge[cnt].w = w;//权值
head[f] = cnt;//更新
cnt++;//自增
}
void dijkstra_heap(int x)//堆优化的Dijkstra
{
dis[x] = 0;//初始化
pq.push(make_pair(dis[x],x));//将源点及其到自身的距离加入堆
while(!pq.empty())//当堆非空时进行循环
{
Pair t = pq.top();//访问堆顶元素
pq.pop();//弹出堆
int point = t.second;//顶点编号
if(book[point] == 0)//如果没被标记
{
book[point] = 1;//标记
for(int i = head[point]; i != -1; i = edge[i].next)//遍历以点point为起点的所有边,i为边的编号
//   源点x到第i条边的终点  源点到点point  点point到终点的权值
if(dis[edge[i].to] > dis[point] + edge[i].w)
{
dis[edge[i].to] = dis[point] + edge[i].w;//更新
pq.push(make_pair(dis[edge[i].to],edge[i].to));//将源点到终点的距离及终点加入堆
}
}
}
}
int main()
{
int n,m,s,f,g,w;
scanf("%d%d%d",&n,&m,&s);//n个顶点,m条边,查询s点
memset(head,-1,sizeof(head));//初始化为-1
for(int i = 1; i <= n; ++i)//初始化为无穷大
dis[i] = inf;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d",&f,&g,&w);//加入的边是共存的,不存在覆盖这一说法
AddEdge(f,g,w);//加入边
}
dijkstra_heap(s);//调用
for(int i = 1; i <= n; i++)//按照题意输出
if(dis[i] == inf)
printf("2147483647 ");
else
printf("%d ",dis[i]);
printf("\n");
return 0;
}


迪杰斯特拉算法步骤:

a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: