迪杰斯特拉

今天的主题:

迪杰斯特拉(单源最短路)


先说说迪杰的思路:

  1. 从所有没有被当过转折点的点中,找出当前距离起点最近的那个点      ——> $\color{green}{O(n)}$ 复杂度}

  2. 用当前的点更新所有剩余点的距离

  3. 共$n$个点,故运行$n$次        ——> $\color{green}{O(n)}$ 复杂度}

  这3步对于朴素算法就都是for循环,其中,3套着1和2,即$O(n^2)$复杂度

  然后堆优化的话可以降到$O(n log_2 n)$ 

  是这个样子,无向图可以用邻接矩阵 $O( n^2 )$ 或 邻接表 $O( nm )$ 存;

        有向图可以用前向星 $O( n+m )$ 存;

无向图


   下面这个是用邻接矩阵打的无向图: 

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,start,q;
const int inf = 100001;
int a[10010][10010];//邻接表 
int d[10010],f[10010];
void zuiduan() { //算法核心
    for (int i = 1;i <= n;i++){
        d[i] = inf;
        f[i] = false;
    }
    d[start] = 0;
    for( int i = 1;i <= n;i++ ){
        int mind = inf;//最短的距离 
        int k;
        for( int j = 1;j <= n;j++ )
            if( !f[j] && d[j] < mind ){
                mind = d[j];//这就是最短的距离 
                k = j;//找到最近的点
            }
        if( mind == inf ) break;//没有最近的点
        f[k] = 1;//该结点已经访问过 
        for( int j = 1; j <= n; j++ )
            if( (!f[j]) && (d[k] + a[k][j] < d[j]) ){
                d[j] = d[k] + a[k][j];//用转折点更新该节点 
            }
    }
}
int main() {
    cin >> n >> m >> start;//结点个数、边的数量、起点 
    int x,y,w;
    for( int i = 1;i <= n;i++ )
        for( int j = 1;j <= n;j++ ){
            if(j == i) a[i][j] = 0;
            else a[i][j] = inf;//初始化 
        }
    for( int i = 1;i <= m;i++ ){
        cin >> x >> y >> w;
        a[x][y] = w;
        a[y][x] = w;//无向图存两遍 
    }
    zuiduan();//迪杰 
    for( int i = 1;i <= n;i++ )
        if( i != start ){
            printf( "点%d的距离为%d\n",i,d[i] );
        }
    return 0;
}
/*一组样例 
7 6 1
1 3 1
1 6 17
7 6 9
6 4 25
2 3 10
3 5 13

*/
无向图迪杰斯特拉

这个被我涂黄的地方是我前一段时间一直不太理解的一个点,不过还好,现在能给出一个像样子的解释:

对,就是这样的当时有点迷糊:

  原本的想法:1->2->3->4 的顺序没问题,但是从 点2 更新 点4 的时候,这个 $a[2][4]$ 不是最短啊,为啥还能对啊?

  解释:用 点2 去更新 点4 的时候,确实不是最值,但用 点3 去更新它的时候就没问题了,

    即对于点A,在所有 $d[x] < d[A]$ 的点中,距离 点A 最近的 点B,必然比 点A 先更新,则此时的 $d[B] + a[B][A]$ 必然是最小的 $d[A]$,

    因为最短路不可能来自一个比 点A 距离起点更远的 点C,使得 $d[C] > d[A]$ 且 $d[C] + a[C][A] < d[A]$,因为这玩意都不满足数学公式。。。

  你这家伙也太费了吧。。。这点简单的东西都想这么久。。。

为什么dij不能处理负边权


 再一次遇到这个问题,正好看到了原因,备注一下

第一轮将B设为最近点,去更新C然而更新不到,故最终d[B] = -1

如果图中都是正数,在第一轮将B设为最近点时,就保证了$AC > AB$,也就是$AC+BC > AB$

所以dij不能解决负边权

有向图 


  然后就是有向图。

  有向图的最好方式就是前向星,主要是占空间小

  见代码: 

#include<iostream>
#include<cstdio>
#define NUM 10010
#define INF 0x7f7f7f
using namespace std;
struct bian{
    int next,to,zhi;
};
bian e[NUM];
int n,m,start,cnt,k;
int head[NUM],d[NUM];
bool v[NUM];
void cun( int x,int y,int w ){
    e[++cnt].next = head[x];
    e[cnt].to = y;
    e[cnt].zhi = w;
    head[x] = cnt;
}
void dijkstra(){
    for( int i = 1;i <= n;i++ ){
        v[i] = 0;
        d[i] = INF;
    }
    d[start] = 0;
    for( int fi = 1;fi <= n;fi++ ){
        int lin = INF;
        for( int i = 1;i <= n;i++ )
            if( !v[i] && d[i] < lin ){
                k = i;
                lin = d[i];
            }
        if( lin == INF ) break;
        v[k] = 1;
        for( int i = head[k];i;i = e[i].next ){
            int p = e[i].to;
            if( !v[p] && d[p] > d[k] + e[i].zhi )
                d[p] = d[k] + e[i].zhi;
        }
    }
}
int main() {
    cin >> n >> m >> start;
    for( int i = 1;i <= m;i++ ){
        int x,y,w;
        cin >> x >> y >> w;
        cun( x,y,w );
    }
    dijkstra();
    for( int i = 1;i <= n;i++ )
        if( i != start ) printf( "点%d到起点的距离为%d\n",i,d[i] );
    return 0;
}
/*一组样例 
7 6 1
1 3 1
1 6 17
6 7 9
6 4 25
3 2 10
3 5 13

输出: 11 1 42 14 17 26
*/
迪杰板子

好的,以上就是朴素的迪杰斯特拉,等我学完堆优化再来补充

 

堆优化


咕了好久的博客终于更了(捂脸)

堆优化了一步,就是让取最短的距离那一步快了,原本的做法是遍历一遍找最短,其中优化后可以将这一步的复杂度降到堆的$log_2 n$

不过每个节点都更新的那个$O(n)$是消不掉了

代码如下

#include<iostream>
#include<cstdio>
#include<queue>
#define NUM 100010
#define INF 0x7f7f
#define int long long
using namespace std;

int n,m,s;
struct bian{
    int to,next;
    long long zhi;
};
bian e[NUM*5];
struct dian{
    int id;
    long long dis;
    bool operator < ( const dian &x ) const{
        return x.dis < dis;
    }
};
int head[NUM];
int cnt;
long long d[NUM];
int vis[NUM];

void add( int x,int y,long long w ){
    e[++cnt].next = head[x];
    e[cnt].to = y;
    e[cnt].zhi = w;
    head[x] = cnt;
}
priority_queue <dian> q;
void dij(){
    for( int i = 1;i <= n;i++ )
        d[i] = 1e10;
    d[s] = 0;
    dian lin;
    lin.dis = 0;lin.id = s;
    q.push(lin);
    dian ff;
    int to,id,dis;
    
    while( !q.empty() ){
        ff = q.top();
        q.pop();
        dis = ff.dis;id = ff.id;
        if( vis[id] ) continue;
        vis[id] = 1;
        for( int i = head[id];i;i = e[i].next ){
            to = e[i].to;
//            if( vis[to] ) continue;
            if( d[to] > d[id] + e[i].zhi ){
                d[to] = d[id] + e[i].zhi;
                lin.dis = d[to];
                lin.id = to;
                if( !vis[to] ) q.push(lin);
            }
        }
    }
}

signed main(){
    
    cin >> n >> m >> s;
    int x,y;
    long long w;
    for( int i = 1;i <= m;i++ ){
        cin >> x >> y >> w;
//        if( x == y ) continue;
        add( x,y,w );
    }
    
    dij();
    
    for( int i = 1;i <= n;i++ )
        cout << d[i] << " ";
        
    return 0;
}















 
堆优化迪杰斯特拉

 

posted @ 2021-08-27 14:56  little_sheep_xiaoen  阅读(223)  评论(0编辑  收藏  举报