目录
单源(某一个点到其余点)
全正边
Dijkstra O(n^2)
1.Dijkstra O(n^2)
const int maxn=1e6+10;
const int inf = 0x3f3f3f3f;
int dist[maxn], box[maxn], mp[1010][1010];
//初始化
memset(dist, 0x3f, sizeof dist);
memset(mp, inf, sizeof mp);
//存图
mp[a][b] = mp[b][a] = min(mp[a][b], value); //双向且考虑重边
void dij(int start)
{
for (int i = 0; i < n; i++) {
ll t = -1;
dist[start] = 0;
for (int j = 1; j <= n; j++) {
if (!box[j] &&(t == -1 || dist[t] > dist[j])) {
t = j;
}
}
box[t] = 1;
for (int j = 1; j <= n; j++) {
dist[j] = min(dist[j], dist[t] + mp[t][j]);
}
}
}
Dijkstra(链式前向星)O(mlogn)
2.Dijkstra(链式前向星)O(mlogn)
#define ll long long
#define pii pair<int,int>
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int h[maxn];//存每个点对应的链的头
int dist[maxn];
bool box[maxn];
//用前初始化规则
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
memset(box, 0, sizeof box);
struct st{
int w; //权值
int ne;//存链的头
int e; //存入图的尾值
}yy[maxn];
int idx = 1; //输入的线段的位置 =>第几条路
void edge(int a, int b, int c) //链式前向星存图 =>从a走到b需要花费c的权值
{
//存内容用
yy[idx].w = c;
//遍历用
yy[idx].e = b;
yy[idx].ne = h[a]; //往回遍历的时候该线段的头是上一个点的位置
h[a] = idx; //a的头指向了连接a的线段的位置(idx
idx++; //该存下一个点了
}
void dijplus(int start, int found) //从那个点开始遍历,要求到哪个点的最短距离
{
dist[start] = 0; //不再是inf
priority_queue<pii, vector<pii>, greater<pii> > q; //由小到大排列=>小顶堆
q.push(make_pair(0,start));
while(!q.empty()) {
int now = q.top().second; //即make_pair(0,start)里start位置的数,0位置是first
q.pop();
if(box[now] == 1) {
continue;
}
box[now] = 1;
for(int i = h[now]; i != -1; i = yy[i].ne) { //向前的遍历
int j = yy[i].e;
dist[j] = min(dist[j], dist[now] + yy[i].w); //更新dist内容
q.push(make_pair(dist[j], j)); //入堆吧你
}
}
cout<<dist[found];
}
存在负边
Bellman-ford O(nm)
3. Bellman-ford O(nm)
特征:可有限制边数,即从i到j最多经历k个边的最短距离;可记录负环情况(码中-1),即某一个环的权值和为负数
const int maxn=1e6+10;
const int inf = 0x3f3f3f3f;
int dist[maxn], backup[maxn];
//初始化
memset(dist, 0x3f, sizeof dist);
//结构体存边
struct st{
int a, b, w;
}edge[maxn];
int bellman_ford()
{
memset(dist,0x3f,sizeof(dist));
dist[1] = 0;
for(int i = 0; i < k; i++) { //只许用k条边,若无要求则是最少n-1即可
memcpy(backup, dist, sizeof(dist)); //backup用来记录上一次的dist状态
for(int j = 0; j < m; j++) { // 枚举所有边
int a = edge[j].a, b = edge[j].b, w = edge[j].w;
dist[b] = min(dist[b], backup[a] + w); // 用上一次记录的更新
}
}
if (dist[n] > 0x3f3f3f3f / 2) { //判断负环
return -1;
}
return dist[n];
}
SPFA O(m)~O(nm)
4.SPFA O(m)~O(nm)
1.用来判断是否存在负环(常用)
链式向前星存图(dijplus里有
//初始化规则
memset(cnt, 0, sizeof cnt);
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
//dist数组的初始化不重要,只需要让判断大小步骤能进入即可证明环是在减小的
bool spfa(int start)
{
queue<int> q;
for(int i = 1; i <= n; i++) {
q.push(i);
}
dist[start] = 0;
box[start] = 1;
while (q.size()) {
int now = q.front();
q.pop();
box[now] = 0; //
for (int i = h[now]; i != -1; i = yy[i].ne) {
int j = yy[i].e;
if (dist[j] > dist[now] + yy[i].w) { //遇到可以更新的情况
dist[j] = dist[now] + yy[i].w;
cnt[j] = cnt[now] + 1;
if (cnt[j] >= n) {
return 1; //存在负环
}
if (!box[j]) {
q.push(j);
box[j] = 1;
}
}
}
}
return 0; //不存在负环
}
2. 求最短路(一般很容易被卡掉,不建议用
//初始化
memset(dist, 0x3f, sizeof dist);
void spfa(int start)
{
queue<int> q;
q.push(start);
dist[start] = 0;
box[start] = 1;
while (q.size()) {
int now = q.front();
q.pop();
box[now] = 0;
for (int i = h[now]; i != -1; i = yy[i].ne) {
int j = yy[i].e;
if (dist[j] > dist[now] + yy[i].w) {
dist[j] = dist[now] + yy[i].w;
if (!box[j]) {
q.push(j);
box[j] = 1;
}
}
}
}
}
//内容和判断负环就差了个cnt数组的记录
多源(任意两点互相走)
floyd(全正边) O(n^3)
5. floyd(全正边) O(n^3)
dp且时间复杂度高,但是全面记录了任意点间的最短路
//初始化
memset(dist, 0x3f, sizeof dist);
//存图
for (int i = 1; i <= n; i++) {
dist[i][i] = 0;
}
dist[a][b] = dist[b][a] = min(dist[a][b], value); //双向且考虑重边,单向的就省去反向即可
void floyd()
{
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
//dist[x][y]即为x点到y点最短路
by yq (有大量参考别人的代码,后续有更深刻的理解再补充