zoukankan      html  css  js  c++  java
  • dijkstra的stl实现(最近觉得挺方便的

    dijkstra的stl实现(最近觉得挺方便的

    stl可作为跳板

                  --- Rujia liu
    
    struct node {
      int dis, id;
      node(int dis = 0, int id = 0) : dis(dis), id(id) {}
      bool operator < (const node& xx) const {
        return dis > xx.dis;
      }
    };
    
    struct Edge{
    	int x,y,val;
    	Edge(int x = 0, int y = 0, int val = 0) : x(x), y(y), val(val) {}
    };//弧 
    //以下为vector版 
    struct Dijkstra {
    	int n,m;
    	vector<Edge> edges;// 边列表
    	vector<int> G[MAXN];// 每个节点出发的边的编号(从0开始
    	bool vis[MAXN];
    	int d[MAXN];//s 到各点距离 
    	int p[MAXN];// 最短路上的一条边
    	
    	void init(int n) {
    		this->n = n;
    		for(int i = 0; i < n; i++) G[i].clear() ;
    		edges.clear() ; 
    	}
    	
    	void add_edge(int x, int y, int val) {
    		edges.push_back((Edge){x, y, val});//没有构造函数的写法
    		m = edges.size() ; 
    		G[x].push_back(m-1);
    	}
    
    	void dijkstra (int s) {
    		priority_queue <node> q;
    		for(int i = 0; i < n; i++) d[i] = INF; 
    		d[s] = 0;
    		memset(vis, 0, sizeof(vis));
    		q.push(node(0,s));//注意id与dis的顺序哦(别问我是怎么知道的
    		while(!q.empty() ) {
    			node tmp = q.top() ; q.pop() ;
    			int u = tmp.id ;
    			if(vis[u]) continue;
    			vis[u] = true;
    			for(int i = 0;i < G[u].size() ; i++) {
    				Edge& e = edges[ G[u][i] ];//引用不加也行 
    				if(d[e.y] > d[u] + e.val ) {
    					d[e.y ] = d[u] + e.val ;
    					p[e.y ] = G[u][i];
    					q.push(node(d[e.y], e.y)) ;
    				}
    			}
    		} 
    	}
    };
    

    用stl+dij实现的应用 & 题目

    要求输出路径的

    统计任意两点之间的路径(包括起点&终点)

    代码呢,就是上面的

    ...
    p[e.y ] = G[u][i];
    ...					
    
    //在struct Dji...里面加
    
      // dist[i]为s到i的距离,paths[i]为s到i的最短路径(经过的结点列表,包括s和t)
      void GetShortestPaths(int s, int* dist, vector<int>* paths) {
        dijkstra(s);
        for(int i = 0; i < n; i++) {
          dist[i] = d[i];
          paths[i].clear();
          int t = i;
          paths[i].push_back(t);
          while(t != s) {
            paths[i].push_back(edges[p[t]].from);
            t = edges[p[t]].from;
          }
          reverse(paths[i].begin(), paths[i].end());
        }
      }
    

    例题:机场快线UVA 11374

    题意: 在一张图中有两种路线,一种是经济线,一种是商业线,经济线可以随便走,但是商业线只能走一次。现在问从起点到终点的最短路是什么。

    注意: 商业线也是双向的

    // 题目相关(一开始写的丢了,只能去代码仓库找了...)
    //作者: Rujia Liu
    Dijkstra solver;
    int d1[maxn], d2[maxn];
    vector<int> paths1[maxn], paths2[maxn];
    
    int main() {
      int kase = 0, N, S, E, M, K, X, Y, Z;
      while(scanf("%d%d%d%d", &N, &S, &E, &M) == 4) {
        solver.init(N);
    
        S--; E--; // 编号从0~N-1
        for(int i = 0; i < M; i++) {
          scanf("%d%d%d", &X, &Y, &Z); X--; Y--;
          solver.AddEdge(X, Y, Z);
          solver.AddEdge(Y, X, Z);
        }
        solver.GetShortestPaths(S, d1, paths1); // S到所有点的距离和路径
        solver.GetShortestPaths(E, d2, paths2); // T到所有点的距离和路径
    
        int ans = d1[E];              // 初始解解为直达距离
        vector<int> path = paths1[E]; // 初始解的station序列
        int midpoint = -1;            // 不坐商业线
    
        scanf("%d", &K);
        for(int i = 0; i < K; i++) {
          scanf("%d%d%d", &X, &Y, &Z); X--; Y--;
          for(int j = 0; j < 2; j++) { // j=0代表商业线坐X->Y,j=1代表Y->X
            if(d1[X] + d2[Y] + Z < ans) {
              ans = d1[X] + d2[Y] + Z;
              path = paths1[X];
              for(int j = paths2[Y].size()-1; j >= 0; j--) // 从Y到T的距离要反过来
                path.push_back(paths2[Y][j]);
              midpoint = X;
            }
            swap(X, Y);
          }
        }
    
        if(kase != 0) printf("
    ");
        kase++;
    
        for(int i = 0; i < path.size()-1; i++) printf("%d ", path[i]+1);
        printf("%d
    ", E+1);//格式有的严格....oj就这样,加油吧
        if(midpoint == -1) printf("Ticket Not Used
    "); else printf("%d
    ", midpoint+1);
        printf("%d
    ", ans);
      }
      return 0;
    }
    

    求单源最短路树

    把p看出父指针,则所有点形成了一棵树(有n-1条边,起点的p值等于它自己,其他点u对应一条边p

    下次再写...留坑待补

    统计路径

    1. 枚举两点间所有的最短路:

      做法: 先求出d, 然后从起点开始出发,但只沿着d[j] = d[i] + w(i,j)

    2. 统计最短路的条数:

      (f[i]表示从i到目标节点的最短路条数

      做法: f[i] = sum(f[j] | d[j] = d[i] + w(i,j) ) 边界条件: f[e终点]为1

    3. 温馨提示: 在最短路中,因为遍历的需要,常常从终点逆推这种情况下就要注意图的单双向了, 双向好说,可以直接按正常的来;单向的话...需要反向

      补:若是需要打印路径,建议在写的时候用一个vector;

    例题: 林中散步UVA10917

    题意:Jimmy打算每天沿着一条不同的路走,而且,他只能沿着满足如下条件的道路(A,B):存在一条从B出发回家的路径,比所有从A出发回家的路径都短,你的任务是计算有多少条不同的路径

    注意,这里出发点为1--,目的点为2--。

    /*熟悉高中恒成立问题的人都知道,题意如下:
    他只想沿着(A,B) 且 d[B] < d[A] 的路径走
    这样,我们在算出d之后创建一个新图: 仅当 d[B] < d[A]时, 加入(A,B)
    这是个DAG, 用DP做*/
    //题目相关 
    int n,m;
    Dijkstra solve;
    int x,y,z;
    int f[MAXN];
    
    int dp(int x) {
    	if(x == 1) return 1;//到家了 
    	
    	int& ans = f[x];//记忆化搜索中常用的,方便
    	if(ans >= 0) return ans;
    	
    	int y;
    	ans = 0; 
    	for(int i = 0; i < solve.G[x].size() ; i++) {
    		y = solve.edges[solve.G[x][i]].y;//双向的就是简单写 
    		if(solve.d[y] < solve.d[x]) ans += dp(y);
    	}
    	return ans;
    }
    
    int main() {
    	while(scanf("%d%d",&n,&m) == 2) {
    		solve.init(n);
    		for(int i = 0; i < m; i++) {
    			scanf("%d%d%d",&x,&y,&z);
    			x--,y--;
    			solve.add_edge(x,y,z); 
    			solve.add_edge(y,x,z);
    		}
    		solve.dijkstra(1);//家
    		memset(f, -1, sizeof(f));
    		//特别注意: 记忆化搜索中的特殊判定!!:要分清"取不到 " 和 “值为0” 
    		//貌似这题没有等于0的,可以设为0 ,并将 ">= 0" 改下 
    		printf("%d
    ", dp(0)); 
    	}
    	return 0;
    }
    

    dij的后记(补):

    1. 存在负边但无负环的最短路, dij不一定能求出解
    2. 无负边的, 多次dij可以求出任意两点之间的路径(如上

    关于用链式前向星实现的最短路打印

    方法很简单,在Edge里加个x , cnt, 分别为边的起点与边的编号, 因此,应该在初始化时,将edge的编号加上,之后的遍历方法就差不多了

  • 相关阅读:
    vue element 表格错位问题
    echarts tooltip 按值的降序显示 tip 信息
    前端 玫瑰花小样式
    echarts X轴数据过多批量显示
    微信js sdk的使用初步理解
    对象 的循环嵌套
    移动端拉起电话请求
    js后加版本号
    数组排序于数组去重
    es6数组的方法
  • 原文地址:https://www.cnblogs.com/tyner/p/10965735.html
Copyright © 2011-2022 走看看