先写篇博客回复一下心情吧
图论太难了,我不配
总结一下这几天学的吧。
朴素版dijkstra算法
时间复杂是 O(n2+m), n 表示点数,m 表示边数
主要用于解决边权为正,存在重边和自环;
参考链接
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int n,m;//n个点,m个边;
int g[N][N];//稠密图用邻接矩阵存图;
int d[N];
bool is[N];
void dijkstra(){
for(int i=0;i<n-1;i++){
int t=-1;
for(int j=1;j<=n;j++)
if(!is[j]&&(t==-1||d[t]>d[j]))t=j;//找出未标记且离起点最近的点
is[t]=true;//标记起点
for(int j=1;j<=n;j++)//用新确定的t点,去更新其他所有点
d[j]=min(d[j],d[t]+g[t][j]);
}if(d[n]==INF)cout<<"-1";//d[n]==INF说明不连通
else cout<<d[n];
}
int main(){
memset(g,INF,sizeof g);
memset(d,INF,sizeof d);//这三行初始化;
d[1]=0;
cin>>n>>m;
int x,y,z;
while(m--){
cin>>x>>y>>z;
g[x][y]=min(g[x][y],z);//解决邻接矩阵存储的有向图重边问题
}dijkstra();
return 0;
}
堆优化版dijkstra
时间复杂度 O(mlogn)), n 表示点数,m 表示边数
参考链接
#include<iostream>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
typedef pair<int,int>PII;
priority_queue<PII,vector<PII>,greater<PII>> que;//定义一个小根堆
const int N=150010,INF=0x3f3f3f3f;
int n,m;
bool is[N];
int e[N],ne[N],w[N],h[N],idx;//邻接矩阵,用来存储稀疏图
int d[N];//某点到n点的距离
void add(int x,int y,int z){
e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;
}
void dijkstra(){
que.push({0,1});//第一个值代表距离,第二个点代表某点
while(que.size()){
PII t=que.top();//取出离源点最近且未标记的点
que.pop();
int dic=t.first,ver=t.second;
if(is[ver])continue;
is[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]>dic+w[i]){//遍历已确定点的每个相邻的点
d[j]=dic+w[i];
que.push({d[j],j});//如果进行了一次更新进放入队列中
}
}
}if(d[n]==INF) cout<<-1;
else cout<<d[n];
}
int main(){
memset(h,-1,sizeof h);
memset(d,INF,sizeof d);
d[1]=0;
cin>>n>>m;
int x,y,z;
while(m--){
cin>>x>>y>>z;
add(x,y,z);
}dijkstra();
return 0;
}//优先队列本身保证了去最近的点,取出该点后,看该点是否被标记过,
//标记过则contonue,未标记则标记,然后遍历所取点的每一个点,更新所有相邻点
//将更新后的点放入队列中;
bellman-ford算法
时间复杂度 O(nm), n 表示点数,m 表示边数
参考链接
一般用于解决有边数限制的最短路问题,边权可以为负
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=510,M=10010,INF=0x3f3f3f3f;
int d[N],back[N];
int n,m,k;
struct edges{
int a,b,w;
}e[M];
void bellman_ford(){
for(int i=0;i<k;i++){//最多通过的点数
memcpy(back,d,sizeof d);//复制上一次的状态
for(int j=0;j<m;j++){//遍历每一条边
int a=e[j].a,b=e[j].b,w=e[j].w;
d[b]=min(d[b],back[a]+w);//对每一条边进行松弛操作
}
}
if(d[n]>INF/2)cout<<"impossible";//因为存在负权边,所以只需大于INF/2,即可判定不连通;
else cout<<d[n];
}
int main(){
memset(d,INF,sizeof d);
d[1]=0;
cin>>n>>m>>k;
int a,b,w;
for(int i=0;i<m;i++){
cin>>a>>b>>w;
e[i]={a,b,w};
}bellman_ford();
return 0;
}
spfa算法(队列优化的Bellman-Ford算法)
时间复杂度 平均情况下 O(m),最坏情况下 O(nm), n表示点数,m 表示边数
参考链接1
参考链接2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e5+10;
int e[N],ne[N],w[N],h[N],idx;
int d[N],n,m;
bool is[N];
void add(int a,int b,int c){//建立链表;
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(){
d[1]=0;
queue<int> q;
q.push(1);
while(q.size()){
auto t=q.front();
is[t]=false;
q.pop();
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]>d[t]+w[i]){
d[j]=d[t]+w[i];
if(!is[j]){
q.push(j);
is[j]=true;
}
}
}
}if(d[n]==0x3f3f3f3f)cout<<"impossible";
else cout<<d[n];
}
int main(){
memset(h,-1,sizeof h);
memset(d,0x3f,sizeof d);
cin>>n>>m;
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}spfa();
return 0;
}
多元路问题的floyd算法
时间复杂度是 O(n3), n表示点数
参考链接
#include<bits/stdc++.h>//多源路问题,用一个邻接矩阵,基于动态规划;
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int g[N][N];
int n,m,w;
void fol(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
}
}
}
int main(){
cin>>n>>m>>w;
memset(g,INF,sizeof g);
for(int i=1;i<=n;i++)g[i][i]=0;
int x,y,z;
while(m--){
cin>>x>>y>>z;
g[x][y]=min(g[x][y],z);//解决重边问题,
}fol();
while(w--){
cin>>x>>y;
if(g[x][y]>INF/2)puts("impossible");
else cout<<g[x][y]<<"\n";
}
return 0;
}