1-1 朴素Dijkstra算法
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//朴素Dijkstra算法,基于贪心算法
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m;
int g[N][N];//存的a->b的距离
int dist[N];//存的是点的距离
bool st[N];//用来判断这这点是否被用过
int dijkstra()
{
memset(dist,0x3f,sizeof dist);//初始化每个点的距离,0x3f3f3f3f相当于正无穷
dist[1]=0;//起点的距离初始化为0,因为起点到起点距离为0
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)//这层循环找还没确定最短路中的最小值
if(!st[j]&&(t==-1||dist[t]>dist[j))
t=j;//更新一下最小
st[t]=true;//把最小值记录一下,说明已经找到了这个点的最短路
for(int j=1;j<=n;j++)//拿t来更新一下其他点的距离
dist[j]=min(dist[j],dist[t]+g[t][j]);//取最小
}
if(dist[n]==0x3f3f3f3f) return -1;//假如终点没有最短路
return dist[n];
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);//初始化每个为0x3f3f3f3f
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
g[a][b]=min(g[a][b],c);//假如有重边,则只要最小的
}
printf("%d\n",dijkstra());//输出从起点到终点的最短路
return 0;
}
/*到达胜利之前无法回头*/
1-2 堆优化的Dijkstra算法
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//堆优化的Dijkstra算法
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;//领接表的实现方式,w存的是权重c
int dist[N];//存的是点的距离
bool st[N];//用来判断这这点是否被用过
void add(int a,int b,int c)//把a->b链接起来
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
memset(dist,0x3f,sizeof dist);//初始化每个点的距离,0x3f3f3f3f相当于正无穷
dist[1]=0;//起点的距离初始化为0,因为起点到起点距离为0
priority_queue<PII,vector<PII>,greater<PII>>heap;//变成小根堆,不懂的话去看前面的stl
heap.push({0,1});//把起点放进堆中,距离为0
while(heap.size())//当堆不为空
{
auto t=heap.top();//取出堆顶
heap.pop();//弹出堆顶
int ver=t.second,distance=t.first;//ver是这个点的位置,distance是这个点的最短路
if(st[ver]) continue;//假如已经放进堆中,则不用重新更新最短路
st[ver]=true;//标记一下已经放进堆中过
for(int i=h[ver];i!=-1;i=ne[i])//单链表的遍历
{
int j=e[i];//取出这个数
if(dist[j]>distance+w[i])//假如这个数不是最小
{
dist[j]=distance+w[i];//更新一下最短路
heap.push({dist[j],j});//把这个点放进堆中
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;//假如终点没有最短路
return dist[n];
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);//初始化每个为空结点
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
add(a,b,c);
}
printf("%d\n",dijkstra());//输出从起点到终点的最短路
return 0;
}
/*到达胜利之前无法回头*/
1.3 双端队列广搜优化的Dijkstra算法 处理边权只有0和1或者其他两个不同的边权
拖拉机
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> pii;
const int N=1e3+10;
bool g[N][N],st[N][N];
int dist[N][N];
int dx[4]={0,1,-1,0},dy[4]={1,0,0,-1};
int bfs(int sx,int sy)//双端队列广搜优化distra算法,定义草的边权为1,空地为0
{
deque<pii> q;
q.push_back({sx,sy});
memset(dist,0x3f,sizeof dist);
dist[sx][sy]=0;
while(q.size())
{
auto t=q.front();
q.pop_front();
if(st[t.x][t.y]) continue;
st[t.x][t.y]=true;
if(!t.x&&!t.y) break;
for(int i=0;i<4;i++)
{
int x=t.x+dx[i],y=t.y+dy[i];
if(x>=0&&x<N&&y>=0&&y<N)
{
int w=0;
if(g[x][y]) w=1;
if(dist[x][y]>dist[t.x][t.y]+w)
{
dist[x][y]=dist[t.x][t.y]+w;
if(!w) q.push_front({x,y});//如果是0,放在队头
else q.push_back({x,y});//如果是1,放在队尾
}
}
}
}
return dist[0][0];//输出到起点的最短路
}
int main()
{
int n,sx,sy;
cin>>n>>sx>>sy;//终点
while(n--)
{
int x,y;
scanf("%d%d",&x,&y);
g[x][y]=true;
}
cout<<bfs(sx,sy);
return 0;
}
2-1 Bellman-Ford算法
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//Bellman-Ford算法
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int dist[N],backup[N];//dist存的是最短路,backup是用来备份
int n,m,k;
struct Edge//建立一个结构体来存三个数,分别是a->b和权重w
{
int a,b,w;
}edges[N];
int bellman_ford()
{
memset(dist,0x3f,sizeof dist);//初始化除起点外距离为正无穷
dist[1]=0;//起点初始化为0
for(int i=0;i<k;i++)
{
memcpy(backup,dist,sizeof dist);//备份dist数组
for(int j=0;j<m;j++)
{
int a=edges[j].a,b=edges[j].b,w=edges[j].w;
dist[b]=min(dist[b],backup[a]+w);//找最小距离,更新最短路
}
}
if(dist[n]>0x3f3f3f3f/2) return 0x3f3f3f3f;//假如不存在最短路
return dist[n];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i]={a,b,w};
}
int t=bellman_ford();
if(t==0x3f3f3f3f) puts("impossible");//假如不存在最短路
else printf("%d\n",t);
return 0;
}
/*到达胜利之前无法回头*/
2-2 spfa算法基本通解所有最短路,正权一般也行,而且时间快
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//spfa算法,也可以解正权的数
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;//领接表的实现方式,w存的是权重c
int dist[N];//存的是点的距离
bool st[N];//用来标记这个点是否在队列中
void add(int a,int b,int c)//把a->b链接起来
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
memset(dist,0x3f,sizeof dist);//初始化每个点的距离,0x3f3f3f3f相当于正无穷
dist[1]=0;//起点的距离初始化为0,因为起点到起点距离为0
queue<int> q;//定义一个队列
q.push(1);//把起点1放进队列中
st[1]=true;//标记1在队列中
while(q.size())//当队列不为空
{
int t=q.front();//取出队头
q.pop();//弹出队头
st[t]=false;//标记这个点已经不在队列中
for(int i=h[t];i!=-1;i=ne[i])//开始遍历
{
int j=e[i];
if(dist[j]>dist[t]+w[i])//判断有没有最小,假如有则后面的点都得更新
{
dist[j]=dist[t]+w[i];//更新每个点的最小值
if(!st[j])//假如这个点不在队列中
{
q.push(j);//则放进队列中
st[j]=true;//标记这个点已经放进了队列中
}
}
}
}
return dist[n];//返回最短路
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);//初始化每个为空结点
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
add(a,b,c);
}
int t=spfa();
if(t==0x3f3f3f3f) puts("impossible");//因为距离还是正无穷,所有没有最短路
else printf("%d\n",t);//输出从起点到终点的最短路
return 0;
}
/*到达胜利之前无法回头*/
2-2-1 spfa判断是否有负环
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//spfa算法,也可以解正权的数
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;//领接表的实现方式,w存的是权重c
int dist[N],cnt[N];//dist存的是点的距离,cnt存的是边数
bool st[N];//用来标记这个点是否在队列中
void add(int a,int b,int c)//把a->b链接起来
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
queue<int> q;//定义一个队列
for(int i=1;i<=n;i++)
{
st[i]=true;//标记每个点都已经在队列中
q.push(i);//把每个点都放进队列中
}
while(q.size())//当队列不为空
{
int t=q.front();//取出队头
q.pop();//弹出队头
st[t]=false;//标记这个点已经不在队列中
for(int i=h[t];i!=-1;i=ne[i])//开始遍历
{
int j=e[i];
if(dist[j]>dist[t]+w[i])//判断有没有最小,假如有则后面的点都得更新
{
dist[j]=dist[t]+w[i];//更新每个点的最小值
cnt[j]=cnt[t]+1;//j的边数加一
if(cnt[j]>=n) return true;//假如j的边数大于n,说明有相同的点,则有负环
if(!st[j])//假如这个点不在队列中
{
q.push(j);//则放进队列中
st[j]=true;//标记这个点已经放进了队列中
}
}
}
}
return false;//否则没有负环
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);//初始化每个为空结点
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
add(a,b,c);
}
if(spfa()) puts("Yes");//有负环
else puts("No");
return 0;
}
/*到达胜利之前无法回头*/
3 Floyd算法处理多源最短路
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//Floyd算法处理多个起点的问题,可以求出任意一个点到另一个任意点的距离
#include<bits/stdc++.h>
using namespace std;
const int N=210,INF=0x3f3f3f3f;
int n,m,Q;
int d[N][N];//定义一个领接矩阵存a->b的距离d
void floyd()//三层循环实现floyd算法
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
int main()
{
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=n;i++)//初始化领接矩阵
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=0;//i==j,说明点的距离为0
else d[i][j]=INF;//其他点定义正无穷
while(m--)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
d[a][b]=min(d[a][b],w);//距离去a->b的较小者
}
floyd();//调用函数
while(Q--)//Q次询问
{
int a,b;
scanf("%d%d",&a,&b);//读入要求的a->b的距离最短路
if(d[a][b]>INF/2) puts("impossible");//假如不存在
else printf("%d\n",d[a][b]);
}
return 0;
}
/*到达胜利之前无法回头*/