最短路问题
最短路问题我想分为3个环节来讲
1.Floyd
2.Dijkstra
3.SPFA
图论:点和边
最短路
多源最短路
一.定义:若干个点到其他点的最短路
\(dis_{i,j} <= dis_{i,k} + dis_{k,j}\)(三角不等式)
当且仅当k在i到j的最短路上,取等
二.算法:
\(Floyed\)(多源最短路只有这一个算法)
1.本质:\(dp\)
2.\(f[i][j]\)表示的起点是 \(i\) ,终点是 \(j\) ,转折点是 \(k\)(当然这里没有被表示出来,只不过是在for循环中有所体现)
3.初始化:若\(i==j\) 那就是0,否则就是\(INF\) ,然后后面输入每条边的价值
for(int k=1;k<=n,k++) // 注意k要放在最外层的循环
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(r[i][j]>r[i][k]+r[j][k])
r[i][j]=r[j][i]=r[i][k]+r[j][k];
单源最短路
一.定义:一个点到其它点的最短路
二.算法
\(Dijkstra\):边权必须为正
贪心求最短点,改用松弛求其他点,如此反复
朴素时间复杂度:\(O(nm)\),找点\(n\)次,松弛边\(m\)次
堆优化\(O((n+m)*log(n+m))\),找点用堆实现
// Dijkstra
void dijkstra(int n,int s){
for(int i=1;i<=n;i++){
int x;//x标记当前最短w的点
int min_dis=INF;//记录当前最小距离
for(int y=1;y<=n;y++){
if(!vis[y] && min_dis>=dis[y]){
x=y;
min_dis=dis[x];
}
}
vis[x]=true;
for(int y=1;y<=n;y++)
dis[y]=min(dis[y],dis[x]+G[x][y]);
}
}
\(SPFA\) 是把可能改变其他店最短路的点放进队列中,取出更新
// SPFA
int dis[N];
bool vis[N];
void SPFA(int S) {
memset(vis, false, sizeof(vis));
memset(dis, INF, sizeof(dis));
dis[S] = 0;
queue<int> Q;
Q.push(S);
while (!Q.empty()) {
int x = Q.front();
Q.pop();
vis[x] = false;
for (int i = head[x]; i != -1; i = edge[i].next) {
int to = edge[i].to;
if (dis[to] > dis[x] + edge[i].dis) {
dis[to] = dis[x] + edge[i].dis;
if (!vis[to]) {
vis[to] = true;
Q.push(to);
}
}
}
}
}
总结
1.判断多源还是单源最短路
2.如果是单源最短路,看看边权是不是为负,非负可以用\(Dijkatra\)或者\(SPFA\),有负边权只能\(SPFA\)
Dijkstra(不能处理负边权,具体见link)
#include<bits/stdc++.h>
#define int long long
const int N = 2e6 + 5;
const int INF = 0x3f3f3f3f;
using namespace std;
inline int read()
{
int s = 0 ;
char ch = getchar();
bool f = true;
while(ch < '0' || ch > '9')
{
if(ch == '-') f = false;
ch = getchar();
}
while(ch >='0' && ch<='9')
{
s = s * 10 + ch - '0';
ch = getchar();
}
return f ? s : ~s + 1;
}
int n,m,s,f[N],vis[N];
struct nod{
int no,w;
bool operator < (const nod &x) const {
return x.w < w;
}
};
priority_queue<nod>q;
struct node{
int v,w,next;
}edge[N];int cnt,head[N];
inline void add(int u,int v,int w)
{
cnt++;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
inline void Dijkstra()
{
for(int i=1;i<=n;i++) f[i] = INF;f[s] = 0;
q.push(nod{s,0});
while(!q.empty())
{
int now = q.top().no;q.pop();
if(vis[now]) continue;vis[now] = 1;
for(int i=head[now];i;i=edge[i].next)
{
int v = edge[i].v , w = edge[i].w;
if(f[v] > f[now] + w)
{
f[v] = f[now] + w;
if(!vis[v]) q.push(nod{v,f[v]});
}
}
}
}
signed main()
{
n = read();m = read();s = read();
for(int i=1;i<=m;i++)
{
int u = read() , v = read() , w = read();
add(u,v,w);
}
// spfa(); TLE
Dijkstra();
for(int i=1;i<=n;i++) cout << f[i] << " ";
return 0;
}
spfa(TLE大法)
#include<bits/stdc++.h>
#define int long long
const int N = 2e6 + 5;
const int INF = 2147483647;
using namespace std;
inline int read()
{
int s = 0 ;
char ch = getchar();
bool f = true;
while(ch < '0' || ch > '9')
{
if(ch == '-') f = false;
ch = getchar();
}
while(ch >='0' && ch<='9')
{
s = s * 10 + ch - '0';
ch = getchar();
}
return f ? s : ~s + 1;
}
int n,m,s,f[N],vis[N];
queue<int>q;
struct node{
int v,w,next;
}edge[N];int cnt,head[N];
inline void add(int u,int v,int w)
{
cnt++;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
inline void spfa()
{
for(int i=1;i<=n;i++) f[i] = INF;f[s] = 0 ;
q.push(s);
while(!q.empty())
{
int now = q.front();q.pop();
vis[now] = 0;
for(int i=head[now];i;i=edge[i].next)
{
int v = edge[i].v , w = edge[i].w;
if(f[v] > f[now] + w)
{
f[v] = f[now] + w;
if(!vis[v]) q.push(v) , vis[v] = 1;
}
}
}
}
signed main()
{
n = read();m = read();s = read();
for(int i=1;i<=m;i++)
{
int u = read() , v = read() , w = read();
add(u,v,w);
}
spfa();
for(int i=1;i<=n;i++)
{
if(s == i)
{
cout << 0 << " ";
continue;
}
if(f[i]) cout << f[i] << " ";
else cout << INF << " ";
}
return 0;
}
凡是过往,皆为序章,前途似海,来日方长