最近在准备机试,刷一些基础题。做到图论的时候把之前学的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最近的点,取出该点后更新距离即可。