算法训练_图论之最短路算法
单源最短路问题
BFS算法不能够求解带权图的最短路问题,如下图
S到B的单源最短路应该是S -> A -> C -> B,若用Bfs,则会出错。
Dijkstra算法
适用范围:只能适用于非负权图。
1.从顶点S开始,S点为0,其余点为正无穷。
2.更新S相邻的点,使A为7 ,B为5,并标记S点。
3.此时5最小,更新B点相邻的点,使C为6,并标记B点。
4.此时A点为7,C点为6,C为最小的点,更新A点为0,并标记C点,后标记最后一个点A结束算法。
此时发现,S点到B点的最短路应该是S -> A -> C-> B ,路径为2,而不是5。
邻接链表的Dijkstra算法的实现
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1001;
const int M = 10001;
int n ,m;
struct edge
{
int v , w , next; //v是连向顶点的编号,w是边的权重,next表示这条边的下一条边的编号
edge(){}
edge(int _v , int _w , int _next)//结构体里的构造函数
{
v = _v;
w = _w;
next = _next;
}
}e[M*2]; //e存放的每一条边
int head[N] , size;
void init()
{
memset(head , -1 , sizeof(head));
size = 0;
}
void insert(int u , int v ,int w)
{
e[size] = edge(v , w , head[u]); //head[u]当前u连出的第一条边
head[u] = size++;
}
void insert2(int u , int v , int w)
{
insert(u , v , w);
insert(v , u , w);
}
int dis[N]; //原点到每一个点的最短路径
bool vis[N]; //点是否被标记过
void dijkstra(int u)
{
memset(vis , false , sizeof(vis));
memset(dis , 0x3f , sizeof(dis)); //dis初始化为正无穷
dis[u] = 0;
for(int i = 0 ; i < n ; i++) //找n次没有标记过的点
{
int mind = 10000000000 , minj = -1;
for(int j = 1 ; j <= n ;j++)
{
if(!vis[j] && dis[j] < mind) //如果点没有被标记且j位置路径小于最小值 ,则更新
{
minj = j;
mind = dis[j];
}
}
if(minj == -1) return; //没有找到标记的点,不是连通图,返回
vis[minj] = true;
for(int j = head[minj] ; ~j ; j = e[j].next) // j != -1 遍历minj出发的每条边
{
int v = e[j].v;
int w = e[j].w;
if(!vis[v] && dis[v] > dis[minj] + w)
{
dis[v] = dis[minj] + w;
}
}
}
}
int main()
{
init();
int u , v ,w;
cin >> n ; //求1->n中的最短路,m是输入数据行数
for(int i = 2 ; i <= n ; i++)
{
for(int j = 1 ; j < i ; j++)
{
cin >> w;
insert2(i , j , w);
}
}
dijkstra(1);
cout << dis[n] << endl;
return 0 ;
}
堆优化的Dijkstra算法的实现
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N = 1001;
const int M = 10001;
int n ,m;
struct edge
{
int v , w , next; //v是连向顶点的编号,w是边的权重,next表示这条边的下一条边的编号
edge(){}
edge(int _v , int _w , int _next)//结构体里的构造函数
{
v = _v;
w = _w;
next = _next;
}
}e[M*2]; //e存放的每一条边
struct Node{
int v , d;
Node(int v , int d):v(v) , d(d){}
bool operator < (const Node &w)const{
return d > w.d;
}
};
int head[N] , size;
void init()
{
memset(head , -1 , sizeof(head));
size = 0;
}
void insert(int u , int v ,int w)
{
e[size] = edge(v , w , head[u]); //head[u]当前u连出的第一条边
head[u] = size++;
}
void insert2(int u , int v , int w)
{
insert(u , v , w);
insert(v , u , w);
}
int d[N];
bool vis[N]; //点是否被标记过
void dijkstra(int u)
{
//memset(vis , false , sizeof(vis));
memset(d , 0x3f , sizeof(d)); //dis初始化为正无穷
d[u] = 0;
priority_queue<Node> q;
q.push(Node(u , 0));
while(!q.empty())
{
u = q.top().v;
q.pop();
if(vis[u]) continue;
for(int j = head[u] ; ~j ; j = e[j].next) // j != -1 遍历minj出发的每条边
{
int v = e[j].v;
int w = e[j].w;
if(!vis[v] && d[v] > d[u] + w)
{
d[v] = d[u] + w;
q.push(Node(v , d[v]));
}
}
}
}
int main()
{
init();
int u , v ,w;
cin >> n >> m; //求1->n中的最短路,m是输入数据行数
while(m--)
{
cin >> u >> v >> w;
insert2(u , v , w);
}
dijkstra(1);
cout << d[n] << endl;
return 0 ;
}
SPFA算法(可计算带负权的图)
序号0为起始点,序号1,2,5入队,队列(125),此时1号值为2;2号值为5;三号值为无穷;4号值为无穷;5号值为9
更新序号3,1出队,3入队,队列(253),此时1号值为2;2号值为5;三号值为1;4号值为无穷;5号值为9
更新序号4,2出队,4入队,队列(534),此时
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e3 +9;
const int M = 1e4 +9;
const int inf = 0x3f3f3f3f;
int dis[N] , in[N];
bool vis[N];
int n , m;
struct edge{
int v , w , next;
edge(){}
edge(int _v , int _w ,int _next)
{
v = _v;
w = _w;
next = _next;
}
}e[M << 1]; //M*2
int head[N] , len;
void init()
{
memset(head , -1 , sizeof(head));
len = 0;
}
void add(int u , int v ,int w)
{
e[len] = edge(v , w , head[u]);
head[u] = len++;
}
void add2(int u , int v ,int w)
{
add(u , v , w);
add(v , u , w);
}
/*bool*/void spfa(int u)
{
memset(vis , false , sizeof(vis));
vis[u] = true;
memset(dis , 0x3f , sizeof(dis));
dis[u] = 0;
//memset(in , 0 ,sizeof(in)); in[u] = 1;
queue<int> q;
q.push(u);
while(!q.empty())
{
u = q.front();
q.pop();
vis[u] = false;
for(int j = head[u] ; ~j ; j = e[j].next)
{
int v = e[j].v;
int w = e[j].w;
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if(!vis[v])
{
q.push(v);
vis[v] = true;
//++in[v]; if(in[v] > n) return true;
}
}
}
}
//return false;
}
int main()
{
init();
int u , v , w;
cin >> n >> m;
while(m--)
{
cin >> u >> v >> w;
add2(u , v , w);
}
spfa(1);
cout << dis[u] << endl;
/*if(spfa(1)) //判断是否有负环
{
cout << "Yes" << endl;
}else
cout << "No" << endl;
*/
return 0;
}
多源最短路径算法
任意两个点之间的最短路径
Floyd算法(不带负边权,不能用邻接表)
利用动态规划的思想,计算给定带权图中两个顶点之间最短路径的算法。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 205, inf = 0x3f3f3f3f;
int g[N][N] , n , m;
void floyd(){
for(int k = 0 ; k <= n ; k++)
{
for(int i = 0 ; i <= n ; i++)
{
for(int j = 0 ; j <= n ; j++)
{
g[i][j] = min(g[i][j] , g[i][k] + g[k][j]);
}
}
}
}
int main()
{
while(cin >> n >> m)
{
memset(g , 0x3f , sizeof(g));//floyd算法起手需要所有点为正无穷,自身点到自身距离为0
for(int i = 1 ; i <= N; i++)
{
g[i][i] = 0;
}
int u , v , w ,s ,t;
while(m--)
{
cin >> u >> v >> w;
u++,v++;
g[u][v] = g[v][u] = min(g[u][v] , w); //自己到自己的距离应该为0
}
floyd();
cin >> s >> t;
s++,t++;
if(g[s][t] > inf/2) cout << "-1" << endl; //判断是否有从起点到终点的路线
else cout << g[s][t] <<endl;
}
return 0;
}
//HDU1874