luogu 3371 SSSP

1 篇文章 0 订阅
1 篇文章 0 订阅
这篇博客介绍了如何解决有向图中从特定起点到所有点的最短路径问题。通过实例展示了SPFA(Shortest Path Faster Algorithm)的基本代码,以及由于其时间复杂度可能造成的问题。博主提出使用SLF(Small Label First)策略优化SPFA,通过双端队列来改善效率。此外,还提到了使用堆优化的Dijkstra算法作为另一种解决方案。
摘要由CSDN通过智能技术生成

Description

给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

Input

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。
接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

Output

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

Sample Input

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

Sample Output

0 2 4 3

Data Size & Hint

Time : 1000MS
Memory : 128M

Data Size:

对于20%的数据:N<=5,M<=15
对于40%的数据:N<=100,M<=10000
对于70%的数据:N<=1000,M<=100000
对于100%的数据:N<=10000,M<=500000

luogu.org非常良心的模板练习

首先,我们来看一个裸的SPFA的代码。虽然理论上SFPA的复杂度为O(KE),并且K一般小于2。可实测证明,这样非常裸的写SPFA会被卡掉。一共10个测试点,TLE了6个。。。写了个读入优化并没有什么用。。捂脸.jpg。。

#include <cstdio>
#include <queue>
using namespace std;
inline int getint()
{
    int r = 0, k = 1;
    char c = getchar();
    for(; c < '0' || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(; c >= '0' && c <= '9'; c = getchar())
        r = r * 10 + c - '0';
    return r * k;
}
const int maxn = 500005;
const int INF = 1009999999;
int n, m, x, y, z, cnte, S, T;
int dis[maxn], h[maxn];
bool vis[maxn];
queue <int> q;
struct Edge
{
    int to, next, w;
} edge[maxn];
void ins(int x, int y, int z)
{
    edge[++cnte].to = y;
    edge[cnte].next = h[x];
    edge[cnte].w = z;
    h[x] = cnte;
}
void SPFA()
{
    for(int i = 1; i <= n; i++)
    {
        dis[i] = INF;
        vis[i] = false;
    }
    dis[S] = 0;
    q.push(S);
    vis[S] = true;
    int now;
    while(!q.empty())
    {
        now = q.front();
        q.pop();
        vis[now] = false;
        for(int i = h[now]; i; i = edge[i].next)
        {
            if(dis[edge[i].to] > dis[now] + edge[i].w)
            {
                dis[edge[i].to] = dis[now] + edge[i].w;
                if(!vis[edge[i].to])
                {
                    q.push(edge[i].to);
                    vis[edge[i].to] = true;
                }
            }
        }
    }
}
int main()
{
    n = getint();
    m = getint();
    S = getint();
    for(int i = 1; i <= m; i++)
    {
        x = getint();
        y = getint();
        z = getint();
        ins(x, y, z);
    }
    for(int i = 1; i <= n; i++)
    {
        if(S == i) dis[i] = 0;
        T = i;
        SPFA();
        if(dis[i] == INF) dis[i] = 2147483647;
    }
    for(int i = 1; i < n; i++)
    {
        printf("%d ", dis[i]);
    }
    printf("%d\n", dis[n]);
    return 0;
}

那么既然裸的SPFA会被卡的惨不忍睹,那么我们考虑将其优化一下。
这里用到的优化是SLF策略,即 Small Label First。设要加入的节点是j,队首元素为i,若dis[j] < dis[i],则将j插入队首,否则插入队尾。
这里就要用到双端队列,用STL中的deque容器实现即可。
具体代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;
inline int getint()
{
    int r = 0, k = 1;
    char c = getchar();
    for(; c < '0' || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(; c >= '0' && c <= '9'; c = getchar())
        r = r * 10 + c - '0';
    return r * k;
}
const int maxn = 500005;
const int INF = 1009999999;
int n, m, x, y, z, cnte, S, T;
int dis[maxn], h[maxn];
bool vis[maxn];
deque <int> q;
struct Edge
{
    int to, next, w;
} edge[maxn];
void ins(int x, int y, int z)
{
    edge[++cnte].to = y;
    edge[cnte].next = h[x];
    edge[cnte].w = z;
    h[x] = cnte;
}
void SPFA()
{
    for(int i = 1; i <= n; i++)
    {
        dis[i] = INF;
        vis[i] = false;
    }
    dis[S] = 0;
    q.push_back(S);
    vis[S] = true;
    int now;
    while(!q.empty())
    {
        now = q.front();
        q.pop_front();
        vis[now] = false;
        for(int i = h[now]; i; i = edge[i].next)
        {
            if(dis[edge[i].to] > dis[now] + edge[i].w)
            {
                dis[edge[i].to] = dis[now] + edge[i].w;
                if(!vis[edge[i].to])
                {
                    if(!q.empty() || dis[q[0]] < dis[edge[i].to])
                    {
                        q.push_back(edge[i].to);
                    }
                    else
                    {
                        q.push_front(edge[i].to);
                    }
                    vis[edge[i].to] = true;
                }
            }
        }
    }
}
int main()
{
    n = getint();
    m = getint();
    S = getint();
    for(int i = 1; i <= m; i++)
    {
        x = getint();
        y = getint();
        z = getint();
        ins(x, y, z);
    }
    for(int i = 1; i <= n; i++)
    {
        if(S == i) dis[i] = 0;
        T = i;
        SPFA();
        if(dis[i] == INF) dis[i] = 2147483647;
    }
    for(int i = 1; i < n; i++)
    {
        printf("%d ", dis[i]);
    }
    printf("%d\n", dis[n]);
    return 0;
}

当然,我们还可以考虑一下用堆优化过的Dijkstra。
代码如下:

/*
Problem: 3371       User: saruka
Memory: 28101K      Time: 639MS
Language: G++       Result: Accepted
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
inline int getint()
{
    int r = 0, k = 1;
    char c = getchar();
    for(; c < '0' || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(; c >= '0' && c <= '9'; c = getchar())
        r = r * 10 + c - '0';
    return r * k;
}
const int INF = 0x3f;
const int maxn = 500005;
int dis[maxn];
bool vis[maxn];
vector <int> G[10001];
int n, m, S;
struct Edge
{
    int u,v,w;
} edge[maxn];
struct HeapNode
{
    int d, u;
    bool operator < (const HeapNode& rhs)const {
        return d > rhs.d;
    } 
};
void dijkstra(int s)
{
    priority_queue<HeapNode> q;
    memset(dis, INF, sizeof(dis));
    dis[s] = 0;
    q.push((HeapNode){0, s}); 
    while(!q.empty())
    {
        HeapNode x = q.top();
        q.pop();
        int u = x.u;
        if(vis[u]) continue;
        vis[u] = true;
        for(int i = 0; i < G[u].size(); i++)
        {
            Edge& e = edge[G[u][i]];
            if(dis[e.v] > dis[u] + e.w)
            {
                dis[e.v] = dis[u] + e.w; 
                q.push((HeapNode){dis[e.v], e.v});
            }
        }
    }
}
int main()
{
    n = getint();
    m = getint();
    S = getint();
    for(int i = 1; i <= m; i++)
    {
        edge[i].u = getint();
        edge[i].v = getint();
        edge[i].w = getint();
        G[edge[i].u].push_back(i);
    }
    dijkstra(S);
    for(int i = 1; i < n; i++)
    {
        if(dis[i] == 0x3f3f3f3f) dis[i] = 2147483647;
        printf("%d ", dis[i]);
    }   
    printf("%d\n", dis[n]);     
    return 0;
}

Powered By Saruka
Copyright © 2016 All Rights Reserved.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值