#include
using namespace std;
#define MAXSIZE 1000
#define NoEdge 1000
int n,m;
int G[MAXSIZE][MAXSIZE]; //权重
int dist[MAXSIZE]; //单源点最短路径
bool visited[MAXSIZE];
int FindMinAdj()
{
int min_i=-1,min=NoEdge; //找离已经有的最短路径结点最近的点
for(int i=0;i<n;i++)
{
if(!visited[i]&&dist[i]<min)
{
min_i=i;min=dist[i];
}
}
return min_i;
}
void Solve(int s)
{
for(int i=0;i<n;i++) //初始化就当做只有s结点到各结点的情况,考虑路径,路径个数等
{
dist[i]=G[s][i];
visited[i]=false;
}
visited[s]=true;
while(true)
{
int min_i=FindMinAdj(); //找离已经确定的最短路径最近的结点
if(min_i==-1) break;
// cout<
visited[min_i]=true;
for(int i=0;i<n;i++)
{
if(!visited[i]&&G[min_i][i]!=NoEdge)
{
if(dist[min_i]+G[min_i][i]<dist[i]) //最短路径优先考虑
{
dist[i]=dist[min_i]+G[min_i][i]; //更新最短路径长度
}
}
}
}
}
int main()
{
int s;
cin>>n>>m>>s;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
G[i][j]=NoEdge;
}
for(int i=0;i<m;i++)
{
int v1,v2,len;cin>>v1>>v2>>len;
G[v1][v2]=len;
G[v2][v1]=len;
}
Solve(s); //求起点s到其他所有顶点的最短路径
return 0;
}
1.代码
#include
using namespace std;
#define MAXSIZE 10005
#define INFI 100000000
int n,m;
typedef struct Node
{
int id;
int W;
}AdjNode,*Adj;
vector<Adj>List[MAXSIZE]; //vector数组实现邻接表
int dist[MAXSIZE];
bool visited[MAXSIZE];
int FindMinAdj()
{
int min_i=-1,min=INFI; //找离已经有的最短路径结点最近的点
for(int i=0;i<n;i++)
{
if(!visited[i]&&dist[i]<min)
{
min_i=i;min=dist[i];
}
}
return min_i;
}
void Dijistra()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) //初始化
{
dist[j]=INFI;
visited[j]=false;
}
dist[i]=0;
while(true)
{
int min_i=FindMinAdj(); //找离已经确定的最短路径最近的结点
if(min_i==-1) break;
visited[min_i]=true; //纳入集合
int len=List[min_i].size();
for(int j=0;j<len;j++) //判断该点受影响的邻接点
{
int t=List[min_i][j]->id;
if(!visited[t]&&dist[min_i]+List[min_i][j]->W<dist[t])
{
dist[t]=dist[min_i]+List[min_i][j]->W;
}
}
}
for(int j=1;j<=n;j++)
{
cout<<dist[j]<<" ";
}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int u,v,w;
//cin>>u>>v>>w;
scanf("%d %d %d",&u,&v,&w);
if(u==v) continue;
Adj a=new AdjNode; //无向图,两个顶点都要进行操作
a->id=v;
a->W=w;
List[u].push_back(a);
Adj b=new AdjNode;
b->id=u;
b->W=w;
List[v].push_back(b);
}
Dijistra();
return 0;
}
1.代码
#include
using namespace std;
#define MAXSIZE 10005
#define INFI 100000000
int n,m;
typedef struct Node
{
int id;
int W;
}AdjNode,*Adj;
struct cmp{ //自定义优先级,返回true表示前者优先级更低(与排序刚好相反)
bool operator()(const Adj a,const Adj b){
return a->W > b->W;
}
};
vector<Adj>List[MAXSIZE]; //vector数组实现邻接表
int dist[MAXSIZE];
bool visited[MAXSIZE];
priority_queue<Adj,vector<Adj>,cmp>que; //按边权值从小到大排序队列
void Dijistra()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) //初始化
{
dist[j]=INFI;
visited[j]=false;
}
dist[i]=0;
Adj t=new AdjNode;
t->id=i;
t->W=0;
que.push(t); //将起点纳入最小堆中,堆顶元素为到当前到起点最近的点
while(!que.empty())
{
Adj t_adj=que.top(); //获得堆顶元素(未收纳的顶点距离起点最近的点)
que.pop();
if(visited[t_adj->id]) continue; //该点已经收纳到结果集合了,获取下一个
int min_i=t_adj->id;
visited[min_i]=true; //纳入集合
int len=List[min_i].size();
for(int j=0;j<len;j++) //判断该点受影响的邻接点
{
int t=List[min_i][j]->id;
if(!visited[t]&&dist[min_i]+List[min_i][j]->W<dist[t])
{
dist[t]=dist[min_i]+List[min_i][j]->W;
Adj p=new AdjNode;
p->id=t;
p->W=dist[t];
que.push(p); //将离起点距离变短的点纳入最小堆
}
}
}
for(int j=1;j<=n;j++)
{
cout<<dist[j]<<" ";
}
cout<<endl;
}
}
int main()
{
//cin>>n>>m;
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int u,v,w;
//cin>>u>>v>>w;
scanf("%d %d %d",&u,&v,&w);
if(u==v) continue;
Adj a=new AdjNode; //无向图,两个顶点都要进行操作
a->id=v;
a->W=w;
List[u].push_back(a);
Adj b=new AdjNode;
b->id=u;
b->W=w;
List[v].push_back(b);
}
Dijistra();
return 0;
}
PTA题目链接:
https://pintia.cn/problem-sets/15/problems/862
#include
using namespace std;
#define MAXSIZE 1000
#define NoEdge 1000
int n,m;
int G[MAXSIZE][MAXSIZE]; //权重1
int Num[MAXSIZE]; //权重2
int dist[MAXSIZE]; //单源点最短路径
int Cnt[MAXSIZE]; //记录最短路径的条数
int Path[MAXSIZE]; //存储最短路径
bool visited[MAXSIZE];
int Que[MAXSIZE]; //权重2最优
int FindMinAdj()
{
int min_i=-1,min=NoEdge; //找离已经有的最短路径结点最近的点
for(int i=0;i<n;i++)
{
if(!visited[i]&&dist[i]<min)
{
min_i=i;min=dist[i];
}
}
return min_i;
}
void Solve(int s,int d)
{
for(int i=0;i<n;i++) //初始化就当做只有s结点到各结点的情况,考虑路径,路径个数等
{
dist[i]=G[s][i];
if(G[s][i]!=NoEdge)
{
Path[i]=s;
Cnt[i]=1;
Que[i]=Num[i]+Num[s];
}
else
{
Cnt[i]=0;
Que[i]=Num[i];
}
visited[i]=false;
}
Cnt[s]=1;
visited[s]=true;
while(true)
{
int min_i=FindMinAdj(); //找离已经确定的最短路径最近的结点
if(min_i==-1) break;
// cout<
visited[min_i]=true;
for(int i=0;i<n;i++)
{
if(!visited[i]&&G[min_i][i]!=NoEdge)
{
if(dist[min_i]+G[min_i][i]<dist[i]) //最短路径优先考虑
{
dist[i]=dist[min_i]+G[min_i][i]; //更新最短路径长度
Que[i]=Que[min_i]+Num[i]; //更新最优权值2
Path[i]=min_i; //更新最短路径
Cnt[i]=Cnt[min_i]; //更新最短路径条数
}
else if(dist[min_i]+G[min_i][i]==dist[i]) //路径长度相等考虑第二个权值
{
Cnt[i]+=Cnt[min_i]; //更新最短路径条数
if(Que[min_i]+Num[i]>Que[i])
{
Que[i]=Que[min_i]+Num[i];
Path[i]=min_i; //更新最短路径
}
}
}
}
}
cout<<Cnt[d]<<" "<<Que[d]<<endl;
int r=Path[d];
cout<<d<<" ";
while(r!=s)
{
cout<<r<<" ";
r=Path[r];
}
cout<<s<<endl;
}
int main()
{
int s,d;
cin>>n>>m>>s>>d;
for(int i=0;i<n;i++)
cin>>Num[i];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
G[i][j]=NoEdge;
}
for(int i=0;i<m;i++)
{
int v1,v2,len;cin>>v1>>v2>>len;
G[v1][v2]=len;
G[v2][v1]=len;
}
Solve(d,s);
return 0;
}
求多源最短路径的算法,只能用邻接矩阵实现
#include
using namespace std;
#define INIFITE 10000
#define MAXSIZE 105
int n,m;
int G[MAXSIZE][MAXSIZE];
int Min_Path[MAXSIZE][MAXSIZE]; //记录i到j的最短路径值,
int path[MAXSIZE][MAXSIZE]; //记录最短路径,递归输出最短路径
bool Floyd() //求任意两点之间的最短距离O(n^3),比对每一个点调用单源点最短路径算法快一点
{
for(int i=1;i<=n;i++) //初始化
{
for(int j=1;j<=n;j++)
{
Min_Path[i][j]=G[i][j];
path[i][j]=j; //初始化i->j一步到位
}
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j&&Min_Path[i][j]<0) return false; //发现负值圈,不能正常解决,返回错误标记
if(Min_Path[i][k]+Min_Path[k][j]<Min_Path[i][j])
{
Min_Path[i][j]=Min_Path[i][k]+Min_Path[k][j];
path[i][j]=path[i][k];
}
}
}
}
for(int i=1;i<=n;i++) //输出最短路径值矩阵
{
for(int j=1;j<=n;j++)
{
if(i==j) Min_Path[i][j]=INIFITE;
cout<<Min_Path[i][j]<<" ";
}
cout<<endl;
}
cout<<"****"<<endl;
for(int i=1;i<=n;i++) //输出最短路径矩阵
{
for(int j=1;j<=n;j++)
cout<<path[i][j]<<" ";
cout<<endl;
}
return true;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
G[i][j]=INIFITE;
}
for(int i=0;i<m;i++)
{
int v1,v2,weight;
cin>>v1>>v2>>weight;
G[v1][v2]=weight;
G[v2][v1]=weight;
}
if(Floyd()) cout<<"yes"<<endl; //没有负权值
int v1,v2;cin>>v1>>v2; //输出v1到v2的路径值和路径
while(v1&&v2) //递归输出最短路径
{
cout<<v1<<"到"<<v2<<"的最短路径值为:"<<Min_Path[v1][v2]<<" ";
// cout<";
int k=v1; //输出v1到v2的最短路径
while(k!=v2)
{
cout<<k<<"-->";
k=path[k][v2];
}
cout<<v2<<endl;
cin>>v1>>v2;
}
return 0;
}
求单源最短路径的算法
时间复杂度高,为O(E*V),比优化后的Dijistra算法差
bellman ford在dijistra算法不能用时才用,比如存在负权边,此时往往需要将其检测出来,如果有负权边,dijistra算法就会陷入负循环(Negative Cycles)中,因为每次总能找到一个更短的路径,就没法得到正确结果
#define INF 0x3f3f3f3f
struct Edge{
int u;//起
int v;//终
int weight;//长度
};
Edge edge[maxm];//用来存储所有的边
int dis[maxn];//dis[i]表示源点到i的最短距离
int n,m;//n个点,m条边
int s;//源点
bool Bellmen_ford()
{
for(int i=1;i<=n;i++)//初始化
dis[i]=INF;
dis[s]=0;//源节点到自己的距离为0
for(int i=1;i<n;i++)//松弛过程,计算最短路径
{
for(int j=1;j<=m;j++)
{
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)//比较s->v与s->u->v大小
dis[edge[j].v]=dis[edge[j].u]+edge[j].weight;
}
}
for(int j=1;j<=m;j++)//判断是否有负边权的边
{
if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)
return false; //设置为负无穷,最后数组中为负无穷的顶点就是负循环中的点
}
return true;
}
bellman-ford的思想和dijkstra很像,其关键点都在于不断地对所有边进行松弛,不加以选择。而最大的区别就在于前者能作用于负边权的情况。其实现思路是在求出最短路径后,判断此刻是否还能对便进行松弛,如果还能进行松弛,便说明还有负边权的边。
最强大,最快的单源最短路径算法
#include
using namespace std;
#define MAXSIZE 10005
#define INFI 100000000
int n,m,k;
typedef struct Node
{
int id;
int W;
}AdjNode,*Adj;
vector<Adj>List[MAXSIZE]; //vector数组实现邻接表
int dist[MAXSIZE];
bool inq[MAXSIZE];
queue<int>node;
void Spfa()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) //初始化
{
dist[j]=INFI;
inq[j]=false;
}
dist[i]=0;
node.push(i); //将起点纳入队列中
inq[i]=true;
while(!node.empty())
{
int min_i=node.front(); //得到队列元素并出队
node.pop();
inq[min_i]=false; //元素出队
int len=List[min_i].size();
for(int j=0;j<len;j++) //对该点有影响的邻接点都进行松弛
{
int t=List[min_i][j]->id;
if(dist[t]>dist[min_i]+List[min_i][j]->W)
{
dist[t]=dist[min_i]+List[min_i][j]->W;
if(!inq[t]) //把不在队列里的元素入队
{
node.push(t);
inq[t]=true;
}
}
}
}
for(int j=1;j<=n;j++)
{
cout<<dist[j]<<" ";
}
}
}
int main()
{
//cin>>n>>m>>k;
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int u,v,w;
//cin>>u>>v>>w;
scanf("%d %d %d",&u,&v,&w);
if(u==v) continue;
Adj a=new AdjNode; //无向图,两个顶点都要进行操作
a->id=v;
a->W=w;
List[u].push_back(a);
Adj b=new AdjNode;
b->id=u;
b->W=w;
List[v].push_back(b);
}
Spfa();
return 0;
}
SPFA算法是Bellman-ford算法的队列优化,比较常用。SPFA算法在负边权图上可以完全取代Bellman-ford算法,另外在稀疏图中也表现良好。但是在非负边权图中,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法,以及它的使用堆优化的版本。通常的SPFA算法在一类网格图中的表现不尽如人意。不是很稳定,不如Dijkstra。
经典练习题:csp认证的第五题:
csp-201903
一般图用邻接表的形式存储会比邻接矩阵快
使用算法时要尽量进行优化