Dijkstra算法优化

最近在做浙江大学复试机试题中遇见的问题一下为链接https://www.nowcoder.com/practice/e372b623d0874ce2915c663d881a3ff2?tpId=63&tqId=29599&tPage=2&rp=2&ru=/ta/zju-kaoyan
问题描述:给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。

输入描述:

输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)

输出描述:

输出 一行有两个数, 最短距离及其花费。

例子:

8 12
1 2 2 2
1 4 6 2
1 6 9 2
2 4 1 2
2 3 30 2
3 8 5 2
4 5 2 2
6 5 3 2
6 7 24 2
5 7 7 2
5 3 8 2
7 8 21 2
1 8

输出

18 10

普通算法:o(n^2)

#include<iostream>
#define N 1001
#define MAX 10000000
int cost[N][N];//Adjacency table
int dis[N][N];
int d[N];//shortest length
int c[N];
bool mark[N];
 
using namespace std;
void init(int n) {
    for (int i = 1; i <= n; i++) {
        d[i] = MAX; c[i] = MAX; mark[i]=false;
    }
    //undirect graph
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j) {
                dis[i][j] = 0; cost[i][j] = 0;
            }
            else {
                dis[i][j] = MAX; dis[j][i] = MAX;
                cost[i][j] = MAX; cost[j][i] = MAX;
            }
        }
    }
}
int main() {
    int n, m;
    while (cin >> n >> m && n > 0) {
        init(n);
        while (m--) {
            //d:distance p:price
            int a, b, d, p; cin >> a >> b >> d >> p;
            if(dis[a][b]>d){
                dis[a][b] = d; dis[b][a] = d;
                cost[a][b] = p; cost[b][a] = p;
            }
            else if(dis[a][b]==d&&cost[a][b]>p){
                cost[a][b]=p;cost[b][a]=p;
            }
        }
        int begin, end; cin >> begin >> end;
        //inital begin vertex
        d[begin] = 0; c[begin] = 0; mark[begin] = true;
        
        int newS = begin;
        //every first loop will add one vertex to array of S
        //so we must have n times loop
        for (int i = 1; i <= n; i++) {
            //update the array of d and c
            for (int j = 1; j <= n; j++) {
              if (mark[j])continue;
                int nd = dis[newS][j];
                int nc = cost[newS][j];
               
                if (nd + d[newS] < d[j]) {
                    d[j] = nd + d[newS]; c[j] = nc + c[newS];
                }
                else if (nd + d[newS] == d[j] && nc + c[newS] < c[j])c[j] = nc + c[newS];
            }
            int min = MAX;
            //update newS
            for (int j = 1; j <= n; j++) {
            	//except the add vertex and the unreached vertex
                if (mark[j])continue;
                if (d[j] == MAX)continue;
                //pic the minist distance
                if (d[j] < min) {
                    min = d[j]; newS = j;
                }
            }
            //add the new vertex
            mark[newS] = true;
        }
        cout << d[end] << " " << c[end] << endl;
    }
}

其实我们在第一次循环中每一个点都要去和其他所有点进行遍历,但是我们中间结果所产生的路径似乎没有被存储

于是我们思考用优先队列q来存储中间路径

于是我们可以看到每一次遍历优先队列里都会包含上一个节点所包含的路径,由于优先队列是小根堆,那么top元素即为我们所求的元素。因此时间复杂度变为(e+n)loge

#include<iostream>
#include<queue>
#include<vector>
#define N 1001
#define MAX 10000000
int cost[N][N];//Adjacency table
int dis[N][N];
int d[N];//shortest length
int c[N];
bool mark[N];

using namespace std;
//use small heap compare
//if d[a]!=d[b] return small heap of distance
//else return small heap of cost
struct cmp
{
	bool operator()(int a, int b) {
		if (d[a] != d[b])return d[a] > d[b];
		else return c[a] > c[b];
	}
};
void init(int n) {
	for (int i = 1; i <= n; i++) {
		d[i] = MAX; c[i] = MAX; mark[i] = false;
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i == j) {
				dis[i][j] = 0; cost[i][j] = 0;
			}
			else {
				dis[i][j] = MAX; dis[j][i] = MAX;
				cost[i][j] = MAX; cost[j][i] = MAX;
			}
		}
	}
}
int main() {
	int n, m;
	while (cin >> n >> m && n > 0) {
		init(n);
		while (m--) {
			//d:distance p:price
			int a, b, d, p; cin >> a >> b >> d >> p;
			if (dis[a][b] > d) {
				dis[a][b] = d; dis[b][a] = d;
				cost[a][b] = p; cost[b][a] = p;
			}
			else if (dis[a][b] == d && cost[a][b] > p) {
				cost[a][b] = p; cost[b][a] = p;
			}
		}
		int begin, end; cin >> begin >> end;
		//inital begin vertex
		d[begin] = 0; c[begin] = 0;
		priority_queue<int, vector<int>, cmp> q;
		q.push(begin);
		while (!q.empty()) {
			int current = q.top();
			q.pop();
			if (mark[current])continue;
			mark[current] = true;
            //if the vertex not vist
            //then loop judge the distance of d
			for(int i=1;i<=n;i++)if (!mark[i])
			{
				//if current d add direct d less than d of direct
				if (d[current] + dis[current][i] < d[i]) {
					d[i] = d[current] + dis[current][i];
					c[i] = c[current] + cost[current][i];
					q.push(i);
				}
			}
		}
		cout << d[end] << " " << c[end] << endl;
	}
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dijkstra算法是一种用于寻找图中最短路径的贪心算法。它的基本思想是从起点开始,逐步扩展到与起点距离越来越远的顶点,直到到达终点为止。在这个过程中,我们会维护一个距离数组,表示从起点到每个顶点的最短距离,同时还需要使用一个集合S来维护已经确定最短路径的顶点集合。具体来说,算法的步骤如下: 1. 初始化:将起点的距离设为0,其余顶点的距离设为无穷大。 2. 从距离数组中选出一个距离最小的顶点u,并将它加入到集合S中。 3. 对于u的每个邻居顶点v,更新v的距离为min(dist[v], dist[u] + w(u, v)),其中w(u, v)表示u到v之间的边的权重。 4. 重复第二步和第三步,直到所有顶点都被加入到集合S中。 Dijkstra算法优化有很多种方法,以下是其中一些常见的优化方式: 1. 使用优化:在第二步中,我们可以使用来维护距离数组中距离最小的顶点,这样可以将时间复杂度从O(n^2)降低到O(m*logn),其中n为顶点数,m为边数。 2. 建立反向图:在第三步中,我们需要遍历每个邻居顶点并更新它们的距离。如果使用邻接矩阵或邻接表来存储图,这个过程可能会比较耗时。因此,我们可以建立原图的反向图,这样就可以直接访问每个顶点的前驱节点,并更新它们的距离。 3. 进行剪枝:在第二步和第三步中,我们可以根据一些启发式规则来剪枝。例如,在选取距离最小的顶点时,我们可以排除掉已经在集合S中的顶点;在更新邻居顶点的距离时,我们可以判断如果新距离大于已知的最短路径,则可以不进行更新。这些优化可以减少算法中不必要的操作,从而提高运行效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值