Dijkstra 算法小结 

By Wine93 2013.11

1. Dijkstra 算法相关介绍

算法阐述:Dijkstra是解决单源最短路径的算法,它可以在O(n^2)内计算出源点(s)到图中任何顶点的最短路,但是该算法不能处理存在负权边的图(证明中会给出)。

Dijkstra一般有2种实现,一种采用邻接矩阵,复杂度为O(n^2),这种实现适用于稠密图 (边多点少),还有一种是采用临接表+heap(可用优先队列代替)实现,实现的复杂度为( m*log(n) )   (m为边数,n为顶点数),该实现适用于稀疏图(边少点多),各有各的优缺,视实际情况选择.

 算法简单证明:Dijkstra有2张表(OPEN,CLOSE),我们可以认为一个表存储已经已经计算出最短路径的顶点(假设U),而另一个则存储没有计算出最短路径的顶点(假设V)。Dijkstra每次都取出具有最短路径的顶点(假设NOW),视其就是该顶点的最短路.因为在当前U表中全部扩展的顶点中,NOW顶点是路径最短的顶点,也就是说,其他当前全部可以到达的顶点中,只有大于或等于NOW顶点,如果 通过这些顶点再到达NOW顶点,势必会比现在NOW顶点的路径长,因为原来就大于等于NOW顶点了, 所以NOW顶点路径长度就是源点到该顶点的最短路径.但是如果存在负权边,则会出现通过其他顶点到达NOW顶点的路径长度小于当前NOW顶点的路径长度,这就是为什么Dijkstra不能处理负权边了(请读者看下面例图,模拟下Dijkstra的执行过程)

2.Dijkstra的相关应用举例

一.基础题 

POJ 1511 Invitation Cards

# include<cstdio>
# include<cstring>
# include<algorithm>
# include<queue>
# include<map>
# include<vector>
# include<utility>
using namespace std; # define LL long long
const LL inf=1LL<<;
const int maxn=;
const int maxm=;
typedef pair<int,LL> node; struct edge
{
int u,v,next;
LL w;
} e[maxm]; struct cmp
{
bool operator()(const node &a,const node &b)const
{
return a.second>b.second;
}
}; int num,head[maxn];
LL dis[maxn];
int n,m;
bool vis[maxn];
priority_queue<node,vector<node>,cmp> q; inline void addedge(int u,int v,LL w)
{
e[num].u=u;
e[num].v=v;
e[num].w=w;
e[num].next=head[u];
head[u]=num++;
} void dijkstra(int s)
{
int i,u,v;
for(int i=;i<=n;i++)
{
dis[i]=inf;
vis[i]=;
}
dis[s]=;
q.push(make_pair(s,dis[s]));
while(!q.empty())
{
u=q.top().first;
q.pop();
if(vis[u])
continue;
vis[u]=;
for(i=head[u];i!=-;i=e[i].next)
{
v=e[i].v;
if(!vis[v]&&dis[u]+e[i].w<dis[v])
{
dis[v]=dis[u]+e[i].w;
q.push(make_pair(v,dis[v]));
}
}
}
} void init()
{
num=;
memset(head,-,sizeof(head));
} int main()
{
//freopen("in.txt","r",stdin);
vector<edge> vec;
int T,u,v,i;
LL w;
LL ans;
scanf("%d",&T);
while(T--)
{
ans=;
vec.clear();
init();
scanf("%d%d",&n,&m);
for(i=;i<=m;i++)
{
scanf("%d%d%I64d",&u,&v,&w);
addedge(u,v,w);
}
dijkstra();
for(i=;i<=n;i++)
ans+=dis[i];
for(i=;i<num;i++)
vec.push_back(e[i]);
init();
for(i=;i<vec.size();i++)
addedge(vec[i].v,vec[i].u,vec[i].w);
dijkstra();
for(i=;i<=n;i++)
ans+=dis[i];
printf("%I64d\n",ans);
}
return ;
}

POJ 1511

POJ 3013 Big Christmas Tre(重点在于思维的转换)

# include<cstdio>
# include<cstring>
# include<map>
# include<vector>
# include<queue>
# include<algorithm>
using namespace std; # define LL long long
typedef pair<LL,int> PII;
# define INF 1LL<<
# define N
# define M int num,head[N];
int vis[N];
LL dis[N];
priority_queue< PII,vector<PII>,greater<PII> >q; struct edge
{
int u,v,next;
LL w;
}e[M]; void addedge(int u,int v,LL w)
{
e[num].u=u;
e[num].v=v;
e[num].w=w;
e[num].next=head[u];
head[u]=num++;
} void dijkstra(int n,int s)
{
int i,u,v;
for(i=;i<=n;i++)
{
dis[i]=INF;
vis[i]=;
}
while(!q.empty()) q.pop();
dis[s]=;
q.push(PII(dis[s],s));
while(!q.empty())
{
u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=;
for(i=head[u];i!=-;i=e[i].next)
{
v=e[i].v;
if(!vis[v]&&dis[u]+e[i].w<dis[v])
{
dis[v]=dis[u]+e[i].w;
q.push(PII(dis[v],v));
}
}
}
} void init()
{
num=;
memset(head,-,sizeof(head));
} int main()
{
//freopen("in.txt","r",stdin);
int T,i,n,m,u,v,flag;
LL w,ans,pri[N];
scanf("%d",&T);
while(T--)
{
flag=;
ans=;
init();
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
scanf("%I64d",&pri[i]);
for(i=;i<=m;i++)
{
scanf("%d%d%I64d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dijkstra(n,);
for(i=;i<=n;i++)
if(dis[i]==INF)
{
flag=;
break;
}
if(!flag) printf("No Answer\n");
else
{
for(i=;i<=n;i++)
ans+=pri[i]*dis[i];
printf("%I64d\n",ans);
}
}
return ;
}

POJ 3013

二.变形题 

POJ 1797 Heavy Transportation

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define N
# define M int num,head[N];
int n,m;
int vis[N]; struct edge
{
int u,v,w,next;
}e[M]; void addedge(int u,int v,int w)
{
e[num].u=u;
e[num].v=v;
e[num].w=w;
e[num].next=head[u];
head[u]=num++;
} int dfs(int u,int limit)
{
int i,v;
vis[u]=;
if(u==n)
return ;
for(i=head[u];i!=-;i=e[i].next)
{
v=e[i].v;
if(!vis[v]&&e[i].w>=limit)
{
if(dfs(v,limit))
return ;
}
}
return ;
} int pass(int limit)
{
memset(vis,,sizeof(vis));
if(dfs(,limit))
return ;
return ;
} int bin(int l,int r)
{
int m;
while(l<=r)
{
m=(l+r)>>;
if(pass(m))
l=m+;
else
r=m-;
}
return r;
} void init()
{
num=;
memset(head,-,sizeof(head));
} int main()
{
// freopen("in.txt","r",stdin);
int cas,T,i,u,v,w;
scanf("%d",&T);
for(cas=;cas<=T;cas++)
{
init();
scanf("%d%d",&n,&m);
for(i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
printf("Scenario #%d:\n",cas);
printf("%d\n\n",bin(,));
}
return ;
}

POJ 1797 二分

# include<cstdio>
# include<cstring>
# include<map>
# include<queue>
# include<vector>
# include<algorithm>
using namespace std; typedef pair<int,int> PII;
# define N
# define M
priority_queue< PII,vector<PII>,less<PII> > q; //pair<dis,u> dis从大到小 int num,head[N];
int dis[N]; struct edge
{
int u,v,w,next;
}e[M]; void addedge(int u,int v,int w)
{
e[num].u=u;
e[num].v=v;
e[num].w=w;
e[num].next=head[u];
head[u]=num++;
} void dijkstra(int n,int s)
{
int i,u,v;
for(i=;i<=n;i++)
dis[i]=;
dis[s]=<<;
q.push(PII(dis[s],s));
while(!q.empty())
{
PII now=q.top();
u=now.second;
q.pop();
if(now.first<dis[u]) continue;
for(i=head[u];i!=-;i=e[i].next)
{
v=e[i].v;
if(dis[v]<min(dis[u],e[i].w))
{
dis[v]=min(dis[u],e[i].w);
q.push(PII(dis[v],v));
}
}
}
} void init()
{
num=;
memset(head,-,sizeof(head));
} int main()
{
//freopen("in.txt","r",stdin);
int cas,T,i,n,m,u,v,w;
scanf("%d",&T);
for(cas=;cas<=T;cas++)
{
init();
scanf("%d%d",&n,&m);
for(i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dijkstra(n,);
printf("Scenario #%d:\n",cas);
printf("%d\n\n",dis[n]);
}
return ;
}

POJ 1797 Dijkstra

POJ 2253 Frogger

# include<cstdio>
# include<cstring>
# include<cmath>
# include<map>
# include<queue>
# include<vector>
# include<algorithm>
using namespace std; # define INF <<
# define N int vis[N];
double dis[N];
double mat[N][N]; struct point
{
double x,y;
}p[N]; double calc(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
} void dijkstra(int n,int s)
{
double mind;
int i,j,u,v;
dis[s]=;
for(i=;i<=n;i++)
{
mind=<<;
for(j=;j<=n;j++)
if(!vis[j]&&dis[j]<mind)
{
mind=dis[j];
u=j;
}
vis[u]=;
for(v=;v<=n;v++)
if(!vis[v]&&dis[v]>max(dis[u],mat[u][v]))
dis[v]=max(dis[u],mat[u][v]);
}
} void init(int n)
{
int i,j;
for(i=;i<=n;i++)
{
vis[i]=;
dis[i]=INF;
for(j=;j<=n;j++)
mat[i][j]=INF;
}
} int main()
{
// freopen("in.txt","r",stdin);
int i,j,n,cas=;
while(scanf("%d",&n)!=EOF&&n)
{
init(n);
for(i=;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
for(i=;i<=n;i++)
for(j=i+;j<=n;j++)
mat[i][j]=mat[j][i]=calc(p[i],p[j]);
dijkstra(n,);
printf("Scenario #%d\n",cas++);
printf("Frog Distance = %.3lf\n\n",dis[]);
}
return ;
}

POJ 2253

将这2题放在一起讲(2题都可用2分解),我们可以利用Dijkstra特殊的结构解决一类问题----单调路径(况且这么叫),所谓单调路径,即为对于任何一条路径, 我们所求的值只会成单调变化

拿POJ1797来说,每经过一条路径,weight的值只会减少(甚至不变),不会增加,看下图(边权值表示 weight,红色顶点为起点,绿色为终点,顶点下数字表示从起始点到达该顶点的weight值)

我们可以发现weight呈现非递增,而我们要求的则是weight的最大值,也就是说我们求的和它的趋势相反都可用Dijkstra来题解(请看下图),这跟Dijkstra可用heap优化是异曲同工(可以认真理解证明)

如果所求值和趋势为同一走向则不能用Dijkstra求解(如最长路)

 三.好题

POJ 3463 Sightseeing

3.个人心得

Dijkstra的变形和应用非常多,需要一定的时间和题量积累,但是只要能深入理解Dijkstra的贪心 策略以及他在单调路径上(况且这么叫)的作用,很多问题都会迎刃而解

[图论]Dijkstra 算法小结的更多相关文章

  1. [图论]Floyd 算法小结

    Floyd 算法小结  By Wine93 2013.11 1. Floyd算法简介 Floyd算法利用动态规划思想可以求出任意2点间的最短路径,时间复杂度为O(n^3),对于稠密图, 效率要高于执行 ...

  2. Cocos2d-x 地图步行实现1:图论Dijkstra算法

    下一节<Cocos2d-x 地图行走的实现2:SPFA算法>: http://blog.csdn.net/stevenkylelee/article/details/38440663 本文 ...

  3. 图论——Dijkstra算法

    图论其实是比较难的一种题型,但是一些模板题,是没有什么太大难度的! 这里给大家带来的是迪杰斯特拉(Dijkstra)算法. 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄 ...

  4. 图论--Dijkstra算法总结

    Key word: ①BFS转换Dijkstra ②其他关系转化为最短路 ③反向建边及反向Dijkstra ④稠密图.稀疏图 ⑤链式前向星 ⑥Vector建图 ⑦超级源点&汇点 详解: 1.B ...

  5. 《算法导论》读书笔记之图论算法—Dijkstra 算法求最短路径

    自从打ACM以来也算是用Dijkstra算法来求最短路径了好久,现在就写一篇博客来介绍一下这个算法吧 :) Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的 ...

  6. 图论基础之Dijkstra算法的初探

         图论,顾名思义就是有图有论.        图:由点"Vertex"和边"Edge "组成,且图分为有向图和无向图(本文讨论有向图),之前做毕业设计的 ...

  7. 图论之Dijkstra算法

    Dijkstra算法是图论中经典的最短路径算法之一,主要用于解决单源最短路径问题. 单源最短路径问题,即求某个源节点到其他各个节点的最短路径. Dijkstra算法采用了贪心算法的思想,如图求1号节点 ...

  8. 图论之最短路径(1)——Floyd Warshall & Dijkstra算法

    开始图论学习的第二部分:最短路径. 由于知识储备还不充足,暂时不使用邻接表的方法来计算. 最短路径主要分为两部分:多源最短路径和单源最短路径问题 多源最短路径: 介绍最简单的Floyd Warshal ...

  9. 数据结构实验之图论七:驴友计划 ( 最短路径 Dijkstra 算法 )

    数据结构实验之图论七:驴友计划 Time Limit: 1000 ms           Memory Limit: 65536 KiB Submit Statistic Discuss Probl ...

随机推荐

  1. Java 集合系列 12 TreeMap

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. [转]Windows的窗口刷新机制

    1.Windows的窗口刷新管理 窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个z-order序列,记录着当前窗口从屏幕底部(假象的从屏幕到眼睛的方向),到屏幕最高层的一个窗口句柄的排序 ...

  3. 通过计算机名访问linux

    1.安装samba 2.设置/etc/samba/smb.conf的 netbois name 配置节的值为你要设置的名称,如 netbois name = mylinux 也可以不设置此项,如果不设 ...

  4. 使用FIDDER 抓包构建请求

    FIDDER 是一个抓包利器,可以抓去浏览器的http请求. 工作原理是: FIDDER 作为代理. 1.在启动fidder时他自动启动服务监听8888端口.     2.启动FIDDER它会自动修改 ...

  5. 如何选择linux 版本

    1.linux 桌面系统,首先选择Ubuntu2.服务器断的linux系统,首选RHEL 或者Centos3.如果对安全性能比较高,选择Debian 或FreeBSD4.需要使用数据库高级服务和电子邮 ...

  6. 使用Join代替In

    我们知道,在sql中使用IN让我们的where子句可以规定多个值.当需要从一个集合中查询包含某几个值的记录的时候,通常我们会选择使用IN来实现,其实,使用JOIN也可以实现这样的功能,而且性能要比IN ...

  7. 10款优秀Vim插件帮你打造完美IDE

    导读 如果你稍微写过一点代码,就能知道“集成开发环境”(IDE)是多么的便利.不管是Java.C还是Python,当IDE会帮你检查语法.后台编译,或者自动导入你需要的库时,写代码就变得容易许多.另外 ...

  8. PHP 长字符串替换操作性能(替换多换注释的代码)

    因为写一个css合并工具,去掉注释时,发现正则表达式很难写,最后,还是用php来实现吧,但是一不小心写出来的代码居然报内存超出可用的128m!! 原因是因为我找到/*和*/之后 $str=substr ...

  9. hdu 4622 Reincarnation

    http://acm.hdu.edu.cn/showproblem.php?pid=4622 用字典树把每一个字符串对应成一个整数 相同的字符串对应到相同的整数上 把所用的串对应的整数放在一个数组里 ...

  10. UVa 11762 - Race to 1

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...