求单源最短路径——Dijkstra算法

Powered by:AB_IN 局外人

介绍

  • 描述:从一个点出发,到达其他顶点的最短路径的长度.
  • D i j k s t r a Dijkstra Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。注意该算法要求图中不存在负权边允许出现环
  • 如果遇到负权边,就需要用 S P F A SPFA SPFA算法
  • 朴素版 D i j s k t r a Dijsktra Dijsktra 的复杂度为 ( O ( n 2 ) ) (O(n^2)) (O(n2)) ,而进行堆(优先队列)优化的 D i j s k t r a Dijsktra Dijsktra 算法,复杂度降到了 ( O ( n + m ) l o g n ) (O(n+m)logn) (O(n+m)logn)
  • 储存边最好用链式前向星,效率高也方便,菜鸡转载过大佬的博客。
  • 关于 D i j k s t r a Dijkstra Dijkstra算法原理,可以去看各位大佬的讲解,真的受益匪浅。如果真的懂了原理,有自己写出来的 D i j k s t r a Dijkstra Dijkstra板子,真挺幸福的!!
  • 一开始的模板是最后更新版本,后面题的代码是不太完善的。

P3371 【模板】单源最短路径(弱化版)

P4779 【模板】单源最短路径(标准版)

板子。先放个没注释的。

#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=2147483647;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w;

void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    q.push( (sa) {0,s});
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)
		{
			if(dis[edge[i].to]>dis[x]+edge[i].w)
			{
				dis[edge[i].to]=dis[x]+edge[i].w;
				q.push( (sa) {dis[edge[i].to],edge[i].to});
			}
		}
	}
}

int main()
{
    memset(head,-1,sizeof(head));
	cin>>n>>m>>s;
	//n=read();m=read();s=read();
	for(int i=1; i<=m; i++){
        cin>>u>>v>>w;
		//u=read();v=read();w=read();
		add_edge(u,v,w);
	}
	dijkstra(s);
	for(int i=1;i<=n;i++) {write(dis[i]);pc(' ');}
	return 0;
}

菜鸡把一些点说一下。
给数组赋inf,直接 m e m s e t memset memset 0x3f就可以了,不用0x3f3f3f3f。
因为0x3f 转换成十进制为63,每个字节都用ASCII码为63的字符去填充,转换成为二进制就是00111111,占一个字节,int型4个字节就是 00111111001111110011111100111111,约等于无穷大。

#include
using namespace std;

namespace IO{//快读快写,自认为好用。。。
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=2147483647;//定义最大值,一般为0x3f3f3f3f。
struct sa{
    int dis;//权值
    int pos;//点(上一个的终点,下一个的起点)
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }
//重载运算符"<"。后面如果按从小到大排,就写">",这个和实际符号相反。
priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
//to为终边,w为权值,next表示与这个边起点相同的上一条边的编号
int head[maxn];//head[i]数组,表示以 i 为起点的最后一条边的编号
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w;

void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;//起点处为0(起点->起点)
    q.push( (sa) {0,s});//起点入队
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;//每个点只访问一次
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)//遍历以x为起点的所有边
		{
			if(dis[edge[i].to]>dis[x]+edge[i].w)
			{
				dis[edge[i].to]=dis[x]+edge[i].w;//更新
				q.push( (sa) {dis[edge[i].to],edge[i].to});
				//将新的{起点->这个点的目前最短距离,终边(即下一次的起点)}放进队列
			}
		}
	}
}

int main()
{
    memset(head,-1,sizeof(head));//全部赋值-1
	cin>>n>>m>>s;
	//n=read();m=read();s=read();
	for(int i=1; i<=m; i++){
        cin>>u>>v>>w;
		//u=read();v=read();w=read();
		add_edge(u,v,w);//有时候会有双向边,要注意。
	}
	dijkstra(s);
	for(int i=1;i<=n;i++) {write(dis[i]);pc(' ');}
	return 0;
}

P1186 玛丽卡

这个题明显复杂的多。
先跑一遍Dijkstra(最短路),然后再去记录单源最短路上经过的边,把路过的边分别去掉,分别跑一边单源最短路取最大值就好啦!
具体操作看代码。

#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int minn=1e3+10;
const int inf=2147483647;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w;

int cou[maxn],flag[minn][minn];
int f,ans;
void dijkstra(){
    s=1;
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    q.push( (sa) {0,s});
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)
		{
			if((!flag[x][edge[i].to])&&dis[edge[i].to]>dis[x]+edge[i].w)
			{//多了的这个条件,就是相当于删一条最短路的边。
				if(!f) cou[edge[i].to]=x;//用链表存最短路的点,最后肯定是con[n]=?,然后下面有个循环从n遍历,就知道每条边的起点与终点了。
				dis[edge[i].to]=dis[x]+edge[i].w;
				q.push( (sa) {dis[edge[i].to],edge[i].to});
			}
		}
	}
}
int main()
{
    memset(head,-1,sizeof(head));
	//cin>>n>>m;
	n=read();m=read();
	for(int i=1; i<=m; i++){
		//cin>>u>>v>>w;
		u=read();v=read();w=read();
		add_edge(u,v,w);
		add_edge(v,u,w);//存双向边
	}
	dijkstra();
	f=1;//一开始f=0,就是为了储存最短路,现在储存完了,就让f等于1。
	for(int i=n;i!=1;i=cou[i]){
        flag[cou[i]][i]=1;
        flag[i][cou[i]]=1;//flag二维数组,一维是起点,一维是终点。就是为了记录这条边不走。
        dijkstra();
        flag[cou[i]][i]=0;//再复原
        flag[i][cou[i]]=0;
        ans=max(ans,dis[n]);//取最大值
    }

    //cout<
    write(ans);pc('\n');
	return 0;
-

P1629 邮递员送信

典型的一类问题:从多到一的最短路变式的路径反转操作——反向建边
什么意思呢?
首先这是有向图,邮递员从一对多跑过去,还要多对一跑回来。一对多直接最短路跑一遍就行了,但多对一怎么办?
我们假设一条最短路径是这样的(s为起点,t为终点) s − > a − > b − > t s->a->b->t s>a>b>t
也就是这么三条边 s − > a a − > b b − > t s->a \\ a->b\\ b->t s>aa>bb>t
那么将 s − > t s->t s>t的最短路反过来 s < − a a < − b b < − t s<-a\\a<-b\\b<-t s<aa<bb<t也是 t − > s t->s t>s的最短路。
也就是说多对一的最短路 ≡ \equiv 反向建图一对多的最短路
上代码!

#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=2147483647;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w;

void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    q.push( (sa) {0,s});
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)
		{
			if(dis[edge[i].to]>dis[x]+edge[i].w)
			{
				dis[edge[i].to]=dis[x]+edge[i].w;
				q.push( (sa) {dis[edge[i].to],edge[i].to});
			}
		}
	}
}

int main()
{
    memset(head,-1,sizeof(head));
	cin>>n>>m;
	//n=read();m=read();s=read();
	for(int i=1; i<=m; i++){
        cin>>u>>v>>w;
		//u=read();v=read();w=read();
		add_edge(u,v,w);
		add_edge(v+n,u+n,w);
	}
	long long ans=0;
	dijkstra(1);
	for(int i=1;i<=n;i++)
        ans+=dis[i];
	dijkstra(1+n);
	for(int i=1+n;i<=n<<1;i++)
        ans+=dis[i];
	cout<<ans<<endl;
	return 0;
}

P1346 电车

与第一个车站建立一条边权为0的边,对于它所相连的其他车站,建立边权为1的边。跑一遍即可。

#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to,w,next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v,int w)
{
    edge[cnt].to = v;
    edge[cnt].w= w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w,t;

void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    q.push( (sa) {0,s});
	while(!q.empty())
	{
		sa ns=q.top();
		q.pop();
		int x=ns.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x]; i!=-1 ; i=edge[i].next)
		{
			if(dis[edge[i].to]>dis[x]+edge[i].w)
			{
				dis[edge[i].to]=dis[x]+edge[i].w;
				q.push( (sa) {dis[edge[i].to],edge[i].to});
			}
		}
	}
}
int main()
{
    memset(head,-1,sizeof(head));
	cin>>n>>s>>t;
	//n=read();m=read();s=read();
	for(int i=1;i<=n;i++){
        cin>>m;
		//u=read();v=read();w=read();
		for(int j=1;j<=m;j++)
        {
            cin>>v;
            if (j==1) add_edge(i,v,0);
            else add_edge(i,v,1);
        }
	}
	dijkstra(s);
	if(dis[t]!=inf) cout<<dis[t]<<endl;
	else cout<<"-1"<<endl;
	return 0;
}

NEFU207 最小树3

无向图。

#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=2147483647;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w;
int main()
{
	while(cin>>n>>m){
        //n=read();m=read();
        s=1;
        memset(head,-1,sizeof(head));
        memset(dis,-1,sizeof(dis));
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=m; i++){
            //u=read();v=read();w=read();
            cin>>u>>v>>w;
            add_edge(u,v,w);
            add_edge(v,u,w);
        }
        for(int i=1; i<=n; i++)
            dis[i]=inf;
        dis[s]=0;
        q.push( (sa) {0,s});
        while(!q.empty())
        {
            sa ns=q.top();
            q.pop();
            int x=ns.pos;
            if(vis[x]) continue;
            vis[x]=1;
            for(int i=head[x]; i!=-1 ; i=edge[i].next)
            {
                if(dis[edge[i].to]>dis[x]+edge[i].w)
                {
                    dis[edge[i].to]=dis[x]+edge[i].w;
                    q.push( (sa) {dis[edge[i].to],edge[i].to});
                }
            }
        }
        cout<<dis[n]<<endl;
	}
	return 0;
}

NEFU208 宫锁珠帘

#include
using namespace std;

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


const int maxn=1e6+10;
const int inf=2147483647;
struct sa{
    int dis;
    int pos;
};
bool operator <(const sa &a,const sa &b) { return a.dis>b.dis; }

priority_queue<sa>q;


struct Edge
{
    int to, w, next;
}edge[maxn];
int head[maxn];
int cnt;
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}


int dis[maxn];
bool vis[maxn];
int n,m,s,u,v,w,t;
int main()
{
	while(cin>>n>>m){
        //n=read();m=read();
        memset(head,-1,sizeof(head));
        memset(dis,-1,sizeof(dis));
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=m; i++){
            //u=read();v=read();w=read();
            cin>>u>>v>>w;
            add_edge(u,v,w);
        }
        cin>>s>>t;
        for(int i=1; i<=n; i++)
            dis[i]=inf;
        dis[s]=0;
        q.push( (sa) {0,s});
        while(!q.empty())
        {
            sa ns=q.top();
            q.pop();
            int x=ns.pos;
            if(vis[x]) continue;
            vis[x]=1;
            for(int i=head[x]; i!=-1 ; i=edge[i].next)
            {
                if(dis[edge[i].to]>dis[x]+edge[i].w)
                {
                    dis[edge[i].to]=dis[x]+edge[i].w;
                    q.push( (sa) {dis[edge[i].to],edge[i].to});
                }
            }
        }
        if(dis[t]!=2147483647) cout<<dis[t]<<endl;
        else cout<<"-1"<<endl;
	}
	return 0;
}

完结。

你可能感兴趣的:(ACM)