学习笔记 (最短路)

dijkstra:求单源最短路

算法步骤:
  1. 找离起点 x x x最近的未讨论过的点 k k k
  2. 判断经过 k k k点,起点x到其他点的距离是否缩短,如缩短
    则更新。将 k k k点标记为已讨论。
  3. 重复进行第 1 1 1步,直到所有点都被讨论过为止。

于是我们可以得出:

d i s [ i ] = m i n ( d i s [ k ] + M a p [ k ] [ i ] ) ( 1 ≤ i ≤ n ) dis[i]=min(dis[k]+Map[k][i]) (1 \leq i \leq n) dis[i]=min(dis[k]+Map[k][i])(1in) k k k是未讨论过的,离起点最近的点

d i s [ i ] dis[i] dis[i]表示从起点到 i i i点最短距离

P S PS PS:为了方便,此篇文章部分采用邻接矩阵存图,建议各位换成更高效的存图方法。

c o d e code code:

void dijkstra(int x){
	for(int i=1;i<=n;i++){        初始化
		mark[i]=false;         mark记录第i号节点是否被讨论过
		dis[i]=Map[x][i];     初始化dis数组,dis[i]用于记录起点x到i的最短距离
	}
	mark[x]=true;           将起点标记为已被讨论,防止走回头路
	do{                            算法核心部分:
		minn=inf;
		k=0;            k记录离x最近的点的编号
		for(int i=1;i<=n;i++)     寻找当前离x最近且未被讨论过的节点
			if((mark[i]==false) && (dis[i]<minn)){
				minn=dis[i];
				k=i;
			}
               讨论经过k点,有没有其他点到x的距离缩短
		if(k>0){
			mark[k]=true;将k号点设置为已被讨论过
			for(int i=1;i<=n;i++)若经过k号点,起点x到i的距离缩短,则更新dis[i]
				if(dis[i]>dis[k]+Map[k][i])
					dis[i]=dis[k]+Map[k][i];
		}
	}while(k>0);
}

时间复杂度 O ( n 2 ) O(n^2) O(n2) 这肯定不行,得想办法优化!

可以发现,每次我们都是要找出离x最近的点的编号,于是我们便可以用强大的stl里的优先队列来完成这个操作,将其优化至 O ( n l o g n ) O(nlogn) O(nlogn)

c o d e code code d i j + 堆 优 化 + 链 式 前 向 星 dij+堆优化+链式前向星 dij++

struct node{
	int num,dis;       num记录节点编号,dis记录起点到该店的最短距离
	bool operator < (const int &a) const{
		return dis>a.dis;	重载< 以dis从小到大排序 
	}
};
void dijkstra(int s){
	for(int i=1;i<=n;i++){初始化
		dis[i]=inf;
		mark[i]=false;
	}
	dis[s]=0;
	priority_queue<node> q;以距离为关键字的小根堆,方便取出目前离起点最近的点
	q.push((node){s,0});
	while(q.size()){
		int x=q.top().num;
		q.pop();
		if(mark[x])  若离起点最近的点x已被讨论过,就跳过
			continue;
		mark[x]=true;
		for(int i=Last[x];i;i=Next[i]){  链式前向星
			int y=End[i];
			if(!mark[y] && dis[y]>dis[x]+len[i]){更新dis[]
				dis[y]=dis[x]+len[i];
				q.push((node){y,dis[y]});
			}
		}
	}
}

SPFA (他死了)

SPFA是Bellman-Ford算法的一种队列实现,减少了不必要的运算。

算法流程:

用一个队列来进行维护。初始时将起点加入队列。每次从队列中取出一个元素,并对他所连接的点进行松弛,若某个点松弛成功(则通过那个点到起点距离缩短),则将其入队。直到队列为空

简单的说就是队列优化的 b e l l m a n − f o r d bellman-ford bellmanford,利用了每个点不会更新次数太多的特点

SPFA的时间复杂度是 O ( k E ) O(kE) O(kE) k k k一般取 2 2 2左右( k k k是增长很快的
函数 a c k e r m a n n ackermann ackermann的反函数, 2 65536 2^{65536} 265536次方也就 5 5 5以下 ),可以处
理负边。

S P F A SPFA SPFA的实现甚至比 D i j k s t r a Dijkstra Dijkstra或者 B e l l m a n F o r d Bellman_Ford BellmanFord还要简单

queue<int> q;
void spfa(int s){    s为起点,求s到图中所有点的距离
	for(int i=1;i<=n;i++)
		dis[i]=inf;
	q.push(s);
	mark[s]=true;
	dis[s]=0;
	while(q.size()){
		int x=q.front();
		q.pop();
		mark[x]=false;
		for(int i=1;i<=n;i++)   讨论与x相连的点
			if(dis[x]+Map[x][i]<dis[i]){  若x通过i点到起点的距离缩短则更新
				dis[i]=dis[x]+Map[x][i];
				if(mark[i]==false){    若不在队列中,入队
					q.push(i);
					makr[i]=true;
				}
			}
	}
}

那么,上文说了 S P F A SPFA SPFA可以处理负权边,那么遇到了负环(负权回路)该怎么处理呢?

用SPFA判断负权回路

让我们来想一想:
S P F A SPFA SPFA算法中,每个点最多进队多少次?

很显然是 n − 1 n-1 n1次,因为对于一个点 x x x,最坏情况下是其余 n − 1 n-1 n1个点都可以将其松弛。

所以如果有一个点进队次数超过了 n n n次,我们就可以认定,该图中存在负权回路,可以果断结束 S P F A SPFA SPFA了。

c o d e code code:

queue<int> q;
int cnt[N]    用于统计每个节点进队次数 
void spfa(int s){
	for(int i=1;i<=n;i++)
		dis[i]=inf;
	q.push(s);
	mark[s]=true;
	dis[s]=0;
	while(q.size()){
		int x=q.front();
		q.pop();
		mark[x]=false;
		for(int i=1;i<=n;i++)
			if(dis[x]+Map[x][i]<dis[i]){
				dis[i]=dis[x]+Map[x][i];
				if(mark[i]==false){
					q.push(i);
					cnt[i]++
					if(cnt[i]==n){
						cout<<"有负权回路";
						return; 
					}
					makr[i]=true;
				}
			}
	}
}

floyd:计算每一对顶点的最短距离 (其实跟dp差不多)

算法思想:
枚举图中每一个顶点k,判断图中其他节点间的距离在经过k后是否缩短,如果是,则用新的更短距离取代原距离

f l o y d floyd floyd的数学表达式:
f [ i ] [ j ] = m i n f [ i ] [ k ] + f [ k ] [ j ] ( 1 ≤ i , j , k ≤ n ) f[i][j]=min{f[i][k]+f[k][j]} (1 \leq i,j,k \leq n) f[i][j]=minf[i][k]+f[k][j](1i,j,kn)

c o d e code code:

for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(Map[i][j]>Map[i][k]+Map[k][j])
            	Map[i][j]=Map[i][k]+Map[k][j];               

f l y o d flyod flyod的时间复杂度为 O ( n 3 ) ! ! ! ! ! O(n^3)!!!!! O(n3)!!!!!

总结:

  1. SPFA很容易被卡,但是在判断负权回路时很好写
  2. 在做最短路问题时,优先考虑dijkstra
  3. floyd纯属娱乐,很少有题用到
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值