1.dijkstra算法
用于计算一个节点到达其他任何结点的最短距离,要求图中不存在负权边
思路:
模板:
const int INF = 0x03f3f3f3f;
const int maxn = 100;
int preV[maxn]; //最短路径树中的前驱结点信息表,preV[x]=y : y是x的前驱结点
int vis[maxn]; //结点是否加入S的标记,1为已加入,2为仍在V-S中
int low[maxn]; //low[i]:从原点到节点i的最短受限路径长度,若i已加入S,则是源到i的最短路长,否则就是以S中的点为媒介的从源到i的最短路径
int G[maxn][maxn]; //邻接矩阵,无路径则数值为INF,对角线为0
int beginV; //原点
int numV; //结点个数
void Dij() //带路径保存
{
vis[beginV]=1;
for(int i = 0 ; i < numV ; i++) //初始S中只有源点,各点的受限距离就是其到源点的距离
{
low[i] = G[beginV][i];
preV[i]=beginV;
}
preV[beginV] = -1; //源点无前驱
int pos = beginV;
for(int i = 1 ; i < numV ; i++) //更新剩下的numV-1个节点
{
for(int j = 0 ; j < numV ; j++)
if(!vis[j] && low[j] > low[pos] + G[pos][j])
{
low[j] = low[pos] + G[pos][j];
preV[j] = pos;
}
int minc = INF;
for(int j = 0 ; j < numV ; j++) //选择最短受限路径
if(!vis[j] && low[j] < minc)
{
minc = low[j];
pos = j;
}
vis[pos] = 1; //下一个被选中的顶点
}
}
int Dij2() //不保存路径
{
vis[beginV]=1;
for(int i = 0 ; i < numV ; i++)
low[i] = G[beginV][i];
for(int i = 1 ; i < numV ; i++)
{
int minc = INF;
int pos,weight=0; //weight - 最短路长
for(int j = 0 ; j < numV ; j++)
if(!vis[j] && low[j] < minc) //由于low[beginV]肯定是最小的0,所以可以不用预处理了
{
minc = low[j];
pos = j;
}
vis[j]=1;
weight += minc;
for(int j = 0 ; j < numV ; j++)
if(!vis[j] && low[j] > low[pos] + G[pos][j])
low[j] = low[pos] + G[pos][j];
}
return weight;
}
2. Floyd算法
所有点之间的最短路
模板:
inf = 0x3fffffff
void floyd()
{
for(intk = 0 ; k < n ; k++)
for(inti = 0 ; i < n ; i++)
if(dis[i][k]< inf)
for(intj = 0; j < n ; j++)
if(i!=j&& dis[i][j] > dis[i][k]+dis[k][j])
dis[i][j]= dis[i][k]+dis[k][j];
}
3.SPFA算法
思路:
建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空
判断有无负环:
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
模板:
SPFA 算法(邻接矩阵)
void spfa(int n ; int src) //顶点个数,起始点
{
que.push(src);
low[src]=0;
vis[src]=1;
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur]=0;
for(int i = 1 ; i <= n ; i++)
if(low[cur]+G[cur][i] <low[i])
{
low[i]=low[cur]+G[cur][i];
if(!vis[i])
{
que.push(i);
cnt[i]++;
vis[i]=1;
}
if(cnt[i] > n)
//出现负环,终止
}
}
}
SPFA算法(邻接表)
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 1<<30;
const int MAXN = 1000005;
int n,m;
struct edge //边结构体,now是有向边的开始结点,next_edge是下一条边的始结点的编号,weight是权重
{
int now;
int next_edge;
int weight;
}e[MAXN]; //边数组
int head[MAXN]; //head[x] : x顶点所在边的编号
int cnt=0;
int low[MAXN]; //储存源点到每个顶点的最短路
int vis[MAXN]; //标记是否在队列里
void add(int x,int y,int v) //加边
{
e[cnt].now = y;
e[cnt].next_edge = head[x]; //这句和第四句不能交换位置
e[cnt].weight = v;
head[x] = cnt++;
}
void spfa(int st)
{
low[st]=0;
queue<int> que;
que.push(st);
vis[st]=1;
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur]=0;
for(int i = head[cur] ; i != -1 ; i = e[i].next_edge)
if(low[e[i].now] > low[cur] + e[i].weight)
{
low[e[i].now] = low[cur] + e[i].weight;
if(!vis[e[i].now])
{
que.push(e[i].now);
vis[e[i].now]=1;
}
}
}
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> m;
memset(vis,0,sizeof(vis));
for(int i = 0 ; i <= n ; i++)
low[i] = INF;
memset(head,-1,sizeof(head));
cnt=0;
for(int i = 0 ; i < m ; i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
spfa(1);
}
return 0;
}