这么蠢的代码是我写的? ——2021年末
================================================
talk is cheap, show me your code
Floyd - Warshall ,时间复杂度 O(n^3)
// Floyed
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
int e[maxLen][maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
// init()
for(int i=1;i<=n;i++) {
dis
for(int j=1;j<=n;j++) {
if(i == j) e[i][j] = 0;
else e[i][j] = inf;
}
}
// Floyed
for(int k=1;k<=n;k++) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++)
if(e[i][j] > e[i][k]+e[k][j])
e[i][j] = e[i][k]+e[k][j];
}
}
// 最短路此时存在数组e中
}
Floyed算法时间复杂度较高,一般仅用于求多源最短路径,可以处理带负权边的图,判断方法:跑两此Floyed,如果第二次仍能缩减某条边,则含有负边权。
Dijkstra优先队列(堆)优化,时间复杂度 O( (M+N)log N)
/* —— 2021年末更新 */
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxLen = 105;
struct Edge {
int to;
int value;
Edge(int t=-1, int v = INF) : to(t), value(v) {}
bool operator < (const Edge& e) {
return value > e.value;
}
};
void Dijkstra_adjList() { // O(M)
// 建立邻接表
int point_num, edge_num;
scanf("%d %d", &point_num, &edge_num); // faster
vector<vector<Edge> > adjList(point_num);
int u, v, value;
for(int num = 0; num < edge_num; ++num) {
scanf("%d %d %d", &u, &v, &value);
adjList[u].push_back(Edge(v, value));
// adjList[v].push_back(Edge(u, value)); 有向图
}
// Dijkstra
vector<bool> vis(point_num, false);
vis[0] = true;
vector<int> dis(point_num, INF);
dis[0] = 0; // 以0号顶点为源点
int nearest = 0;
while(--point_num) {
int nextNearest, minDis = INF;
for(Edge e : adjList[nearest]) {
if(vis[e.to]) continue;
if(dis[nearest] + e.value < dis[e.to]) {
dis[e.to] = dis[nearest] + e.value;
if(dis[e.to] < minDis) {
minDis = dis[e.to];
nextNearest = e.to;
}
}
}
vis[nextNearest] = true;
nearest = nextNearest;
}
for(int i : dis)
printf("%d ", i);
printf("\n");
}
void Dijkstra_martix() {
// 建立邻接表
int point_num, edge_num;
scanf("%d %d", &point_num, &edge_num); // faster
vector<vector<int> > adjMatrix(point_num, vector<int>(point_num, INF));
int u, v, value;
for(int num = 0; num < edge_num; ++num) {
scanf("%d %d %d", &u, &v, &value);
adjMatrix[u][v] = value;
}
vector<bool> vis(point_num, false);
vis[0] = true;
vector<int> dis(point_num, INF);
dis[0] = 0; // 以0号顶点为源点
int nearest = 0;
while(--point_num) {
int nextNearest, minDis = INF;
for(int i=1; i<adjMatrix.size(); ++i) { // point_num 在变
if(vis[i]) continue;
if(adjMatrix[nearest][i] == INF) continue;
if(dis[nearest] + adjMatrix[nearest][i] < dis[i]) {
dis[i] = dis[nearest] + adjMatrix[nearest][i];
if(dis[i] < minDis) {
minDis = dis[i];
nextNearest = i;
}
}
}
vis[nextNearest] = true;
nearest = nextNearest;
}
for(int i : dis)
printf("%d ", i);
printf("\n");
}
int main()
{
// 邻接表 存 图 O(M) ?
Dijkstra_adjList();
// 邻接矩阵 存 图 O(N^2)
Dijkstra_martix();
return 0;
}
// Floyed
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
struct Edge {
int to,w;
Edge(int too=-1,int ww=inf) {
to = too;
w = ww;
}
friend bool operator < (const Edge e1,const Edge e2) {
return e1.w > e2.w;
}
};
vector< Edge> v[maxLen];
int dis[maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
//init
memset(dis,inf,sizeof dis);
dis[1] = 0; //以1为源点
// vector.clear();
//建立邻接表
int st,ed,w;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&st,&ed,&w);
v[st].push_back(Edge(ed,w));
v[ed].push_back(Edge(st,w));
}
//Dijkstra
priority_queue< Edge> q;
q.push(Edge(1,0));
while(!q.empty()) {
int u = q.top().to;
for(int i=0;i<v[u].size();i++) {
int t = v[u][i].to;
if(dis[t] > dis[u]+v[u][i].w) {
dis[t] = dis[u]+v[u][i].w;
q.push(Edge(t,dis[t]));
}
}
q.pop();
}
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
return 0;
}
算法思想:用已经确定最短长度的路径去缩减未确定的路径,直到所有路径都确定最短(即不能再继续缩减)
Dijkstra,用于求单源最短路径,不能处理带负权边的图
Bellman - Ford,时间复杂度O(NM)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
struct Edge {
int u,v,w;
}e[maxLen];
int dis[maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(dis,inf,sizeof dis);
dis[1] = 0; //以1为源点
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
//bellman
bool flag = true;
for(int i=1;i<=n-1 && flag;i++) {
flag = false;
for(int j=1;j<=m;j++) {
if(dis[e[j].v] > dis[e[j].u]+e[j].w) {
dis[e[j].v] = dis[e[j].u]+e[j].w;
flag = true;
}
}
if(!flag) break;
}
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
printf("\n");
for(int j=1;j<=m;j++) { //判断是否含有负权边
if(dis[e[j].v] > dis[e[j].u]+e[j].w) {
dis[e[j].v] = dis[e[j].u]+e[j].w;
printf("含有负权边\n");
}
}
return 0;
}
Bellman - Ford用于求单源最短路,可以处理带负边权的图,
Bellman实际上是通过边缩减,有固定的缩减次数上限,而Dijkstra实际上是通过点缩减,如果有负权环,则会无限制的进行while队列循环。
SPFA,时间复杂度最坏: O(MN)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
struct Edge {
int to,w;
Edge(int too,int ww) {
to = too;
w = ww;
}
};
vector< Edge>v[maxLen];
int dis[maxLen],num[maxLen];
bool vis[maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
bool flag = false;
memset(dis,inf,sizeof dis);
memset(num,0,sizeof num);
memset(vis,false,sizeof vis);
dis[1] = 0; //以1为源点
int st,ed,w;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&st,&ed,&w);
v[st].push_back(Edge(ed,w));
v[ed].push_back(Edge(st,w));
}
queue< int> q;
q.push(1);
vis[1] = true;
while(!q.empty()) {
int k = q.front();
num[k]++;
if(num[k] > n) {
printf("含有负权回路\n");
flag = true;
break;
}
for(int i=0;i<v[k].size();i++) {
Edge t = v[k][i];
if(dis[t.to] > dis[k]+t.w) {
dis[t.to] = dis[k]+t.w;
if(!vis[t.to]) {
q.push(t.to);
vis[t.to] = true;
}
}
}
vis[k] = false;
q.pop();
}
if(!flag) {
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
}
return 0;
}
SPFA用于求单源最短路,可以处理带负权边的图
SPFA实际上就是Bellman - ford的队列优化 。与Dijkstra在写法上很相似,区别在于保证同一个时刻队列中不会出现两个相同的点。
习题:
POJ - 1806 Currency Exchange Currency Exchange POJ - 1860 (Bellman判断正环)_MengHao的博客-CSDN博客