由Prim学会Dijkstra

最近在准备机试,刷一些基础题。做到图论的时候把之前学的Prim和Dijkstra复习一下。虽然一个是最小生成树,一个是最短路,但算法思路非常像,除了扩展点时的条件不同,其他过程几乎一样。这里就用两道洛谷普及/提高-难度的题进行对比记忆。

Prim算法及其堆优化在我很久之前的博客写过,如果不会可以去学习Prim及其堆优化,本文只对Prim和Dijkstra进行对比。

 

先贴题和AC代码,对比在后面,不需看题的直接跳到最后。

首先是最小生成树的题

P1194 买礼物

AC源码

#include 
#include 
#include 
#include 
#include 
#define INF 1<<15
#define white 0
#define black 1
using namespace std;

int a,b;
int graph[505][505];
int color[505];
int dis[505];

struct node{
    int num;
    int len;
    node(){}
    node(int a,int b):num(a),len(b){}
};

struct comp{
    bool operator()(node a,node b){
        return a.len>b.len;
    }
};

void prim(int x){
    priority_queue,comp> q;
    //dis[x]=0;
    q.push(node(x,a));
    while(!q.empty()){
        node temp=q.top();
        q.pop();
        dis[temp.num]=min(temp.len,min(dis[temp.num],a));
        color[temp.num]=black;
        for(int i=1;i<=b;i++){
            if(color[i]==white){
                if(graph[temp.num][i]>=dis[i])continue;
                q.push({i,graph[temp.num][i]});
            }
        }
    }
}


int main(){
    cin>>a>>b;
    int temp;
    for(int i=1;i<=b;i++){
        for(int j=1;j<=b;j++){
            cin>>temp;
            if (temp==0)graph[i][j]=graph[j][i]=a;
            else graph[i][j]=graph[j][i]=temp;
        }
    }

    for(int i=1;i<=b;i++)dis[i]=INF;
    prim(1);
    int res=0;
    for(int i=1;i<=b;i++)res+=dis[i];
    cout<

然后是最短路的题

P4779 单源最短路经

AC源码

#include 
#include 
#include 
#include 
#include 
#define white false
#define black true
using namespace std;

struct path{
    int des;
    long long int len;
    path(){}
    path(int a,long long int b):des(a),len(b){}
};
vector graph[100005];
long long int dis[100005];
bool color[100005];
int n,m,s;
struct comp{
    bool operator()(path a,path b){
        return a.len>b.len;
    }
};

void dijkstra(int x){
    priority_queue,comp> q;
    q.push({x,0});
    vector::iterator it;
    while(!q.empty()){
        path temp=q.top();
        q.pop();
        if(color[temp.des])continue;
        color[temp.des]=black;
        dis[temp.des]=min(temp.len,dis[temp.des]);
        for(it=graph[temp.des].begin();it!=graph[temp.des].end();it++){
            if(color[it->des])continue;
            if(dis[temp.des]+it->len>=dis[it->des])continue;
            q.push({it->des,dis[temp.des]+it->len});
        }
    }

}

int main(){
    cin>>n>>m>>s;
    int u,v,w;
    for(int i=0;i>u>>v>>w;
        graph[u].push_back({v,w});
    }
    memset(dis,127,sizeof(dis));
    dijkstra(s);
    for(int i=1;i<=n;i++){
        cout<

那么,我们将两道题的算法部分单独拉出来对比:

//Prim:
void prim(int x){
    priority_queue,comp> q;
    //dis[x]=0;
    q.push(node(x,a));
    while(!q.empty()){
        node temp=q.top();
        q.pop();
        dis[temp.num]=min(temp.len,min(dis[temp.num],a));
        color[temp.num]=black;
        for(int i=1;i<=b;i++){
            if(color[i]==white){
                if(graph[temp.num][i]>=dis[i])continue;
                q.push({i,graph[temp.num][i]});
            }
        }
    }
}

//Dijkstra
void dijkstra(int x){
    priority_queue,comp> q;
    q.push({x,0});
    vector::iterator it;
    while(!q.empty()){
        path temp=q.top();
        q.pop();
        if(color[temp.des])continue;//最优性剪枝
        color[temp.des]=black;
        dis[temp.des]=min(temp.len,dis[temp.des]);
        for(it=graph[temp.des].begin();it!=graph[temp.des].end();it++){
            if(color[it->des])continue;
            if(dis[temp.des]+it->len>=dis[it->des])continue;//最优性剪枝
            q.push({it->des,dis[temp.des]+it->len});
        }
    }

}

我们可以看到,两段代码非常相像,都是从点x进行扩展,并使用了堆优化

除去剪枝部分,两段代码最大的不同在于扩展代码:

Prim:q.push({i,graph[temp.num][i]});

Dijkstra:q.push({it->des,dis[temp.des]+it->len});

因为Prim解决的问题是最小生成树,那么在扩展时只需考虑被扩展点和当前点(即生成树)的距离即可。插入优先队列后,每次取出的是当前队列中距离当前生成树最近的点,直接将该点连入生成树。

而Dijkstra解决的是图中所有点距离起点x的最短距离,那么在扩展时,不仅仅要考虑当前点与被扩展点之间的距离(it->len),还要考虑当前点与起点x的距离(dis[temp.des]),两者相加才是被扩展点距离起点x的距离(dis[temp.des]+it->len)。这样,在插入优先队列后,每次取出的点都是当前队列中距离起点x最近的点,取出该点后更新距离即可。

你可能感兴趣的:(算法学习)