Dijkstra
Dijkstra 算法是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。
特点:是从起点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
举例说明:
Dijkstra 算法在运行的过程中维持的关键信息是一组节点的集合 S S S。
从源节点 s s s 到该集合中每个节点之间的最短路径已经被找到。
算法重复从节点集 V − S V-S V−S 中选择最短路径估计最小的节点 u u u ,将 u u u 加入到集合 S S S ,然后对所有从 u u u 出发的边来进行松弛。
(我们可以用一个最小优先队列 Q = V − S Q=V-S Q=V−S)
上图的解释:
源结点s为左边的结点。每个结点中的数值为该结点的最短路径的估计值,加了阴影的边表示前驱值。
黑色的结点属于S集合,白色的结点属于Q。
注意:要求图中不存在负权边。
朴素:
void INIT()
{
memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); memset(p,0,sizeof(p));
vis[sta]=1;
for(int i=1;i<=n;++i) d[i]=a[sta][i];
for(int i=1;i<=n;++i) p[i]=sta;
d[sta]=0; p[sta]=0;
//sta(start)起点
}
void Dijkstra()
{
int k; init();
for(int i=1;i<=n-2;++i)
{
m=max1;
for(int j=1;j<=n;++j)
{
if(d[j]<m && !vis[j])
{m=d[j]; k=j;}
}
if(m==max1) break;
vis[k]=1;
for(int j=1;j<=n;++j)
if(d[k]+a[k][j]<d[j])
{
d[j]=d[k]+a[k][j];
p[j]=k;
}
}
printf("%d",d[end]);
return ;
}
堆优化:
#define INF 0x3f3f3f3f
struct EDGE{int to,dis,next;} e[MAXM];
struct node
{
int dis,pos;
bool operator <( const node &x ) const {return x.dis < dis;}
//重定向
};
std::priority_queue< node > q;
int n,m,s;
int adj[MAXN],dis[MAXN],cnt;
bool vis[MAXN];
void addedge(int u,int v,int d) //链式前向星
{
++cnt;e[cnt].dis=d;e[cnt].to=v;e[cnt].next=adj[u];adj[u]=cnt;
}
void Dijkstra()
{
dis[s]=0; q.push(( node ){0,s}); //先将源点入队列
while(!q.empty())
{
node temp=q.top(); q.pop();
int x=temp.pos;
if(vis[x]) continue; //对未访问节点进行扫描
vis[x]=1;
for(int i=adj[x];i;i=e[i].next)
{
int y=e[i].to;
if(dis[y]>dis[x]+e[i].dis) //三角不等式,松弛
{
dis[y]=dis[x]+e[i].dis;
if(!vis[y])
q.push( (node){dis[y], y} ); //邻接节点入队列
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=n;++i) dis[i]=INF; //初始化
for(int i=0;i<m;++i)
{
int u,v,d; scanf("%d%d%d",&u,&v,&d);
addedge(u,v,d);
}
Dijkstra();
for(int i=1;i<=n;++i) printf("%d ",dis[i]);
return 0;
}
做题感悟:
- 有的题目是在问题中套着最短路的模型,要分析出题目中所求的“路径”,然后使用Dijkstra算法。
- 在“三角不等式”的部分注意,有些题目是运用Dijkstra的思想,但最后所求答案的计算公式并是不“三角不等式”。