earthhouge 2020-07-19
算法思想:
给定一张n个点,m条边的图,起点为s。求起点s到图中所有点的最短路径(单源最短路。dis[i]表示从起点到i的最短距离。vis[i]表示此点是否已被标记确定为最短。
1、初始化dis[s]=0,其余结点dis为正无穷大。
2、找到一个未被标记的、dis[x]最小的结点x,标记x。
3、扫描结点x的所有出边(x,y,z),若dis[y]>dis[x]+z,则用dis[x]+z更新dis[y]。
4、重复2、3步骤,知道所有点均被标记。
复杂度:O(n^2)。
优化:优先队列(堆)。
?```#include <queue>``` ?```priority_queue< pair<int,int> >q;```
https://www.luogu.com.cn/problem/P4779
对dis数组进行维护,加快获取最小值以及执行一条边的扩展与更新。
代码:
#include <cstdio> #include <queue> #include <cstring> using namespace std; typedef long long int ll; const int maxn=1000005; ll n,m,s,tot,head[maxn],x,dis[maxn]; priority_queue< pair<ll,ll> >q;//优先队列(二叉堆)两维:第一维为dis[i]相反数第二维结点编号 struct node{ ll nxt,to; ll w; }t[maxn];//链式前向星存图 void add(const int u,const int v,const int w){ t[++tot].to=v;t[tot].w=w; t[tot].nxt=head[u];head[u]=tot; }//加边 bool vis[maxn]; void dijkstra(){ memset(dis,0x3f,sizeof(dis));//初始边无限大 memset(vis,0,sizeof(vis));//结点初始均为访问 dis[s]=0;//起点到自己距离为零 q.push(make_pair(0,s));//起点进堆 while(q.size()!=0){// x=q.top().second;q.pop();//初始结点入队 if(vis[x])continue;//如果走过,直接跳过 vis[x]=1;//标记已访问 for(ll i=head[x];i;i=t[i].nxt){ ll y=t[i].to,z=t[i].w; if(dis[y]>dis[x]+z){//松弛 dis[y]=dis[x]+z;//更新起点到y最短路 q.push(make_pair(-dis[y],y));//d[y]相反数入队,转小根堆。 } } } } int main(){ scanf("%lld%lld%lld",&n,&m,&s);//n个点,m条边,起点为s。 for(int i=1;i<=m;i++){ ll a,b,c; scanf("%lld%lld%lld",&a,&b,&c); add(a,b,c); } dijkstra(); for(int i=1;i<=n;i++){ printf("%lld ",dis[i]); } }