zoukankan      html  css  js  c++  java
  • Dijkstra 算法

    Dijkstra 算法小总结(未完)

    前言:最短路问题在各种比赛中还是挺常见的,所以呢....还是很有必要好好弄懂的qwq

    于是在洛谷上先看了模板题重新get了Dijkstra+堆优化的程序代码,然后做了几道最短路的题。现在做个小总结

    PS:因为是刷题总结,所以就不像题解那样写得比较详细了。且因为是Dijkstra专题,所以以下所有最短路都是用Dijkstra+堆优化(或变式)来编


    目录:

    • 模板题

    • 应用&变式题


    模板题:

    1、【模板】单源最短路径(标准版)

    2、【模板】单源最短路径(弱化版)

    (两道模板题的唯一区别:数据范围不一样,弱化版的数据范围还要大一点?!)

    如下给出标准版模板题的程序代码

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,s,tot;
    int head[200001],dis[200001];
    bool vis[200001];
    struct edge{
    	int to,net,dis;
    }e[200001];
    
    struct node {
    	int dis,pos;
    	bool operator <( const node &x )const {
    		return x.dis<dis;
    	}
    };
    
    std::priority_queue<node> q;
    
    inline void add_edge(int u,int v,int w) {
    	e[++tot].dis=w;
    	e[tot].to=v;
    	e[tot].net=head[u];
    	head[u]=tot;
    }
    
    inline void dijkstra() {
    	dis[s]=0;
    	q.push((node) {
    		0,s
    	});
    	while(!q.empty()) {
    		node tmp=q.top();
    		q.pop();
    		int x=tmp.pos,d=tmp.dis;
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=e[i].net) {
    			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(register int i=1;i<=n;i++) dis[i]=0x7fffffff;
    	for(register int i=1;i<=m;i++) {
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		add_edge(u,v,w);
    	}
    	dijkstra();
    	for(register int i=1;i<=n;i++) printf("%d ",dis[i]);
    	return 0;
    }
    

    应用&变式:

    这道题题目简明扼要,摆明了最短路嘛!只是需要注意是构建无向图,其他的直接套模板即可

    再啰嗦一下构建无向图:就是重复两次调用add_edge(链式前向星建图),程序段如下:

    inline void add_edge(int u,int v,int w) {
    	e[++tot].dis=w;
    	e[tot].to=v;
    	e[tot].net=head[u];
    	head[u]=tot;
    }
    for(register int i=1;i<=m;i++) {
    	int u,v,w;
    	scanf("%d%d%d",&u,&v,&w);
    	add_edge(u,v,w);
    	add_edge(v,u,w);
    }
    

    联系一下生活实际,道路肯定是双向的嘛!所以是无向图

    但是这道题还是有迷惑性的,一定要看完题,不能看到前面就直接认为是单纯的无向图最短路。题目的最后一句话:“一条从s至t的路线,使得经过道路的拥挤度最大值最小”,所以是求一定意义上的“最长路”!

    所以我们只需要将板子里松弛操作,即只需要将原来的求和改成取max

    Dijkstra(松弛操作)代码段如下:

    inline void dijkstra() {
    	dis[s]=0;
    	q.push((node) {
    		0,s
    	});
    	while(!q.empty()) {
    		node tmp=q.top();
    		q.pop();
    		int x=tmp.pos,d=tmp.dis;
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=e[i].net) {
    			int y=e[i].to;
    			if(dis[y]>max(dis[x],e[i].dis)) {  //松弛操作
    				dis[y]=max(dis[x],e[i].dis);
    				if(!vis[y]) {
    					q.push((node) {
    						dis[y],y
    					});
    				}
    			}
    		}
    	}
    }
    

    法(1):题目中“A最少需要多少钱使得转账后B收到100元”提示我们这道题与模板题相对比,起点和终点相反。所以我们可以将A、B互换回来,再求最短路(最小花费)

    法(2):和上道题一样,也是修改松弛操作来实现这道题。因为题目给的转账方式是百分数,所以边权应该是相乘而不是相加

    注意一下法(2)中的一些小修改:

    ①优先队列中的重载小于号做了修改

    ②初始化的时候dis赋为0即可(负数应该也可),不要赋成正无限大

    (亲试,如上两点不同时更改,样例都过不了QAQ,也有可能是我有些地方没注意,欢迎指出)

    下面给出完成代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,s,t;
    int tot,head[1000001];
    double dis[1000001];
    bool vis[1000001];
    
    struct edge {
    	int to,net;
    	double dis;
    }e[1000001];
    
    struct node {
    	double dis;
    	int pos;
    	bool operator < ( const node &x ) const {
    		return x.dis>dis;
    	}
    };
    
    std::priority_queue<node> q;
    
    inline void add_edge(int u,int v,int w) {
    	e[++tot].dis=1.0-w/100.0;
    	e[tot].to=v;
    	e[tot].net=head[u];
    	head[u]=tot;
    }
    
    inline void dijkstra() {
    	dis[s]=1.0;
    	q.push((node) {
    		1.0,s
    	});
    	while(!q.empty()) {
    		node tmp=q.top();
    		q.pop();
    		int x=tmp.pos;
    		if(vis[x]) continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=e[i].net) {
    			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",&n,&m);
    	for(register int i=1;i<=n;i++) dis[i]=0;
    	for(register int i=1;i<=m;i++) {
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		add_edge(u,v,w);
    		add_edge(v,u,w);
    	}
    	scanf("%d%d",&s,&t);
    	dijkstra();
    	printf("%.8lf",100.0/dis[t]);
    	return 0;
    }
    

    感觉这题思路还是有点难度的(可能单纯因为我vegetable吧QAQ)

    题目简述:

    有n个点,m条路,对于每一条路给出两个端点(双向边哦)和这条路的花费、流量

    要求求出1~n的道路的最大流量与最小花费的比×1e6(向下取整)

    解题思路:

    这题跟普通Dijkstra的区别就是有两个权值,所以我们先枚举1~1000(流量值最大1000)的流量值,然后每次都跑Dijkstra(起点始终是1,终点始终是n)

    跑完一次我们就判断是否1~n连通,如果连通就比较答案的最大值

    完整code:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,a,b,c,f,tot,ans;
    int dis[100010],vis[100010],head[100010];
    priority_queue<pair<int,int> > shan;
    
    struct node {
    	int to,net,liu,val;
    } e[100010];
    
    inline void add(int u,int v,int w,int l) {
    	e[++tot].to=v;
    	e[tot].net=head[u];
    	e[tot].liu=l;
    	e[tot].val=w;
    	head[u]=tot;
    }
    
    inline void dijkstra(int l) {  //基本就是Dijkstra板子qvq 
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[1]=0;
    	shan.push(make_pair(0,1));  //按照一维排序 
    	while(!shan.empty()) {
    		int x=shan.top().second;
    		shan.pop();
    		if(vis[x]==1) continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=e[i].net) {
    			int v=e[i].to;
    			if(e[i].liu<l) continue;  //如果流量值小于枚举值,不用执行了 
    			if(dis[v]>dis[x]+e[i].val) {
    				dis[v]=dis[x]+e[i].val;
    				shan.push(make_pair(-dis[v],v));
    			}
    		}
    	}
    }
    
    int main() {
    	scanf("%d%d",&n,&m);
    	for(register int i=1;i<=m;i++) {
    		scanf("%d%d%d%d",&a,&b,&c,&f);
    		add(a,b,c,f);  //双向边 
    		add(b,a,c,f);
    	}
    	for(register int li=1;li<=1000;li++) {  //枚举流量值 
    		dijkstra(li);
    		if(dis[n]!=0x3f) ans=max(ans,int(double(li*1000000/dis[n])));  //如果连通就比较最大值 
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    To be continued....


  • 相关阅读:
    转载 从最简单的vector中sort用法到自定义比较函数comp后对结构体排序的sort算法
    TYVJ P1081 最近距离 Label:这不是分治!!!
    TYVJ P1086 Elevator Label:dp
    数字图像处理的三个层次
    栅格化是什么意思?
    图像基本知识
    修改了天空盒子但是点play还是没变原因
    地形编辑
    Bmp8位图像亮度如何调整?
    bmp图像作业笔记
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13054138.html
Copyright © 2011-2022 走看看