【算法小结】Dijkstra迪杰斯特拉算法

“ Ctrl AC!一起 AC!”

目录

ONE.求起点到达其他点的最短距离

TWO.求具体的最短路径

THREE.求最短路径共有几条

FOUR.考虑新增边权或点权求最优


Dijkstra一般用于解决单源最短路问题

ONE.求起点到达其他点的最短距离

策略:

设置集合S存放已访问的顶点,然后进行若干次以下操作,直到点全访问完毕。

1.每次从未访问的点中选择一个距离起点最近的点,加入S。

2.以新加入的点为介点进行路径优化。

具体实现:

1.bool型vis数组表示某点有没有访问过。

2.int型d数组代表起点到达该点的最短距离,初始化为inf,起点到起点距离则为零。

邻接矩阵版:

int n,G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
int d[MAXV]; //起点到某点的最短路径长度
bool vis[MAXV]; //初始为false表示没走过
void Dijkstra(int s){
	fill(d,d+MAXV,INF); //初始距离边为无限大
	d[s]=0; //起点到起点的距离为零
	for(int i=0;i<n;i++){ //循环n次
		int u=-1,MIN=INF;
		for(int j=0;j<n;j++){
			if(vis[j]==false&&d[j]<MIN){
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1) return;
		vis[u]=true;
		for(int v=0;v<n;v++){
			if(vis[v]==false&&G[u][v]!=INF&&d[u]+G[u][v]<d[v]){
				d[v]=d[u]+G[u][v];
			}
		}
	}
}

邻接表版:

struct Node{
	int v,dis; //v为边的目标顶点,dis为边权
};
vector<Node> Adj[MAXV]; //Adj[u]存放从顶点u出发可以到达的所有顶点
int n; //顶点数
int d[MAXV];
bool vis[MAXV];
void Dijkstra(int s){
	fill(d,d+MAXV,INF);
	d[s]=0;
	for(int i=0;i<n;i++){
		int u=-1,MIN=INF;
		for(int j=0;j<n;j++){
			if(vis[j]==false&&d[j]<MIN){
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1) return;
		vis[u]=true;
		for(int j=0;j<Adj[u].size();j++){
			int v=Adj[u][j].v;
			if(vis[v]==false&&d[u]+Adj[u][j].dis<d[v]){
				d[v]=d[u]+Adj[u][j].dis;
			}
		}
	}
}

注意点:

1.此方法不能解决边权为负数的情况

2.可以用最小堆或priority_queue解决第二循环---找最小的d[u],减少复杂度

TWO.求具体的最短路径

第一步:我们先在优化的时候记录一下前驱结点

int n,G[MAXV][MAXV];
int d[MAXV];
int pre[MAXV]; //pre[v]表示从起点到v的路径上 v的前一个结点
bool vis[MAXV];
void Dijkstra(int s){
	fill(d,d+MAXV,INF);
	for(int i=0;i<n;i++) pre[i]=i; //初始化为自己
	d[s]=0;
	for(int i=0;i<n;i++){
		int u=-1,MIN=INF;
		for(int j=0;j<n;j++){
			if(vis[j]==false&&d[j]<MIN){
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1) return;
		vis[u]=true;
		for(int v=0;v<n;v++){
			if(vis[v]==false&&G[u][v]!=INF&&d[u]+G[u][v]<d[v]){
				d[v]=d[u]+G[u][v];
				pre[v]=u;
			}
		}
	}
}

第二步:得到前驱结点后可以通过递归或栈得到正确的路径:

void DFS(int s,int v){ //s为起点,v为当前的点
	if(v==s){
		printf("%d\n",s);
		return;
	}
	DFS(s,pre[v]);
	printf("%d\n",v);
}

THREE.求最短路径共有几条

只需新增一个num数组:num[i]表示起点到点i的最短路劲的条数,初始化num[起点]为1,其他为0

d[u]+G[u][v]<d[v]时,num[u]=num[v]

d[u]+G[u][v]==d[v]时,num[u]+=num[v]

for(int v=0;v<n;v++){
	if(vis[v]==false&&G[u][v]!=INF){
		if(d[u]+G[u][v]<d[v]){
			d[v]=d[u]+G[u][v];
			num[v]=num[u];
		}
		else if(d[u]+G[u][v]==d[v]){
			num[v]+=num[u];
		}
	}
}

FOUR.考虑新增边权或点权求最优

1.新增边权:

如果一个二维数组a[u][v]表示u到v的新增边权,则同原边权一样开个数组表示c[u]表示从起点到u的新边权总和的最大值或最小值。

例:cost[u][v]表示u到v这条路的路费,要求费用最小的最短路径(先满足最短),我们新开一个数组c[],c[u]表示从起点到点u的最小花费,初始化c[起点]=0,其他为INF。

for(int v=0;v<n;v++){
	if(vis[v]==false&&G[u][v]!=INF){
		if(d[u]+G[u][v]<d[v]){
			d[v]=d[u]+G[u][v];
			c[v]=c[u]+cost[u][v];
		}
		else if(d[u]+G[u][v]==d[v]&&c[u]+cost[u][v]<c[v]){
			c[v]=c[u]+cost[u][v];
		}
	}
}

2.新增点权:

例:weight[u]表示点u的物资数量,新增一个数组w[],w[u]表示从起点到点u可以收集到的最多的物资;初始化时w[s]=weight[u],其余为0。

for(int v=0;v<n;v++){
	if(vis[v]==false&&G[u][v]!=INF){
		if(d[u]+G[u][v]<d[v]){
			d[v]=d[u]+G[u][v];
			w[v]=w[u]+weight[v];
		}
		else if(d[u]+G[u][v]==d[v]&&w[u]+weight[v]>w[v]){
			w[v]=w[u]+weight[v];
		}
	}
}

感谢阅读!!!

“ Ctrl AC!一起 AC!”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ctrl AC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值