最短路专题

最短路

Floyd-Warshall算法

用途:求多源最短路(即任意两点间的最短路)

在这里插入图片描述
如上图,我们要求得不同城市之间的最短距离,就可以列出简单的距离矩阵
在这里插入图片描述
然后按照a->b的距离如果小于(a->c)+(c->b)的距离,则将a->b的距离替换为(a->c)+(c->b)的距离逻辑进行遍历。
核心代码如下:

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

这里要注意的点是,在最外面的for语句的k,一定要放在判断语句的中间变量位置。

缺点是会覆盖掉原来的边长,如果要进行多次运算,则无法找回原来的边长,且无法计算负权制,代码时间复杂度O(n3)。

Dijkstra算法

迪彻斯特算法的特点在于,它增加了vis数组,以用于松弛操作,松弛操作也是dijkstra最显著的特点,可以大幅减少时间复杂度,增加了dis数组,以确定距离,而非在原来的边长上进行覆盖,在运算后仍能保持原边长数组不变。
Dijkstra算法算是贪心思想实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
在这里插入图片描述
在这里插入图片描述
按照这样的逻辑进行运算
代码示例如下

void dijk(int s) {//计算所有点到s的最短距离,存到dis数组里
	memset(dis, 0x3f, sizeof(dis));//距离设为最大
	memset(vis, 0, sizeof(vis));//初始化为全部未松弛状态
	dis[s] = 0;//对自己的距离为0
	while (1) {
		int k = 0;//找最近的未松弛节点
		for (int j = 1;j <= n;j++) {
			if (!vis[j] && dis[j] < dis[k])
				k = j;//将最近的未松弛节点设置为k
		}
		if (!k)return;//若没有最近未松弛节点,说明全部松弛,结束
		vis[k] = 1;//松弛完成
		for (int j = 1;j <= n;j++) {
			if (dis[j] > dis[k] + map[k][j]) {//进行判断,替代dis长度
				dis[j] = dis[k] + map[k][j];
			}
		}
	} 
}

缺点同样是不能计算负权值,代码时间复杂度O(n2),使用链式前向星或者优先队列进行优化可以优化为O(nlogn)。

Bellman-Ford算法

这个算法比dijkstra要简单一些,减少了确定松弛的操作,对每一个点进行反复松弛,每实施一次松弛操作, 就会有一些顶点已经求得其最短路,即这些顶点的最短路的“估计值”变为“确定值”。优点很明显,可以进行负权值的运算,但同时时间复杂度也会有所上升。

代码示例:

for(int k = 1 ; k <= n - 1 ; k ++)
{
    for(int i = 1 ; i < m ; i ++)
    {
        if(dis[v[i]] > dis[u[i]] + w[i])
            dis[v[i]] = dis[u[i]] + w[i] ;
    }
}

题目示例:

会长东东想娶酋长的女儿,但酋长要求他给一定数额金钱的聘礼。除了金钱外,酋长也允许用部落里其他人的某物品加上一点钱作为聘礼。而其他人的物品也可以通过指定的另外一些人的某物品加上一些金钱获得。但是部落里的每个人有一个等级。整个交易过程涉及的人的等级只能在一个限定的差值内。问会长东东最少需要多少金钱才能娶到酋长女儿。假定每个人只有一个物品。

Input
输入第一行是两个整数M,N(1 <= N <= 100),依次表示地位等级差距限制和物品的总数。接下来按照编号从小到大依次给出了N个物品的描述。每个物品的描述开头是三个非负整数P、L、X(X < N),依次表示该物品的价格、主人的地位等级和替代品总数。接下来X行每行包括两个整数T和V,分别表示替代品的编号和"优惠价格"。

Output
输出最少需要的金币数。

Sample Input
1 4
10000 3 2
2 8000
3 5000
1000 2 1
4 200
3000 2 1
4 200
50 2 0

Sample Output
5250

例: 最高最低等级差不超过1,共4个物品 酋长的女儿 等级3 要求现金10000元 或甲的物品+8000元 或乙的物品+5000元 甲的物品 等级2 要求现金1000元 或丙的物品+200元 乙的物品 等级2 要求现金3000元 或丙的物品+200元 丙的物品 等级2 要求现金50元 在这个例子中,最少花费方案是:买丙的东西(50)换乙的东西(+200)换酋长女儿(+5000)一共5250元。

代码示例:

#include<iostream>
#include<cstring>
#include<stack>
#include<queue>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdio>
#include<set>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
const int maxn = 1000;
ll e[1020][1020];
int vis[1020], dis[1020];
ll lv[10000],temp,minn;
int n, m, t, x;
void dijk(int s, int l, int r) {
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	dis[s] = 0;
	while (1) {
		int k=-1,minn=0x3f3f3f3f;
		for (int j = 0;j <= n;j++) {
			if (!vis[j] && dis[j] < minn) {
				k = j;
				minn = dis[j];
			}
		}
		if (k==-1)return;
		vis[k] = 1;
		for (int j = 0;j <= n;j++) {
			if (lv[j] >= l && lv[j] <= r) {
				if (dis[j] > dis[k] + e[k][j]) {
					dis[j] = dis[k] + e[k][j];
				}
			}
		}
	}
}
void Init() {
	memset(e, 0x3f3f3f3f, sizeof e);
	for (int i = 1;i <= m;i++) {
		e[i][i] = 0;
	}
}
void input() {
	cin >> m >> n;
	for (int i = 1;i <= n;i++) {
		cin >> e[0][i] >> lv[i] >> t;
		for (int j = 1;j <= t;j++) {
			cin >> x;
			cin >> e[x][i];
		}
	}
}
int main() {
	Init();
	input();
	int ans = 0x3f3f3f3f;
	for (int i = lv[1] - m;i <= lv[1];i++) {
		dijk(0, i, i + m);
		ans = min(dis[1], ans);
	}
	cout << ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值