采购特价商品——最短路一题5解

 📒博客首页:永遇乐金枪鱼的博客

🎉欢迎关注🔎点赞👍收藏⭐️留言📝

❤️ :热爱Java与算法学习,期待一起交流!

🙏作者水平很有限,如果发现错误,求告知,多谢!

🌺有问题可私信交流!!!

👻高校算法学习社区:​​​​​​高校算法学习社区

一起加入刷题内卷大军,还可以加入专属内卷群
 

目录

Dijkstra朴素版

Dijkstra堆优化版

bellman-ford算法

spfa(与dijkstra堆优化基本一样)

floyd

补充


P1744 采购特价商品 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述:中山路上有n(n<=100)家店,每家店的坐标均在-10000~10000之间。其中的m家店之间有通路。若有通路,则表示可以从一家店走到另一家店,通路的距离为两点间的直线距离。

输入格式:

共n+m+3行:

第1行:整数n

第2行~第n+1行:每行两个整数x和y,描述了一家店的坐标

第n+2行:整数m

第n+3行~第n+m+2行:每行描述一条通路,由两个整数i和j组成,表示第i家店和第j家店之间有通路。

第n+m+3行:两个整数s和t,分别表示原点和目标店

输出格式:

仅一行:一个实数(保留两位小数),表示从s到t的最短路径长度。

数据量:n<=100,m<=1000

分析:题目给定店的坐标以及两家店之间是否有路,所以需要用到两点距离公式,距离或着由公式可以得到这个权值为非负,并且这个距离可以为小数,所以应该用double声明;题目说到“若有通路,则表示可以从一家店走到另一家店”,说明了这是无向图。由于输出只有一行(从s到t的最短路径长度),则说明给定两个点求其最短路径,那么可以认为给定一个图G和1个源点s,对于图中任意点v,求s到v的最短距离。由数据量及第四大点的各算法复杂度可得,五种方法都能被用于解决这道题。

下面是5种方法解答本题(两点距离公式只在第一种处写了,完整代码给出第一第二种方法,剩下三种给出核心伪代码供大家思考):

Dijkstra朴素版

声明一个f[u][v]用于存放点u到v的距离,无向图则用f[u][v]=f[v][u],dis[i]用于存放始点到点i的最短距离,两个数组都需要初始化成无穷大,然后寻找距离始点最近且未访问过的顶点的邻接节点,如果可以松弛则进行松弛操作。

#include <bits/stdc++.h>
#define INF 0x7f7f7f7f
using namespace std;
typedef long long LL;
double dis[105];
double f[105][105];
int n,m,s,t;
int vis[105],x[105],y[105];

inline int read() {
	int date=0,w=1;
	char c=getchar();
	while(c<'0' || c>'9') {
		if(c=='-') w=-1;
		c=getchar();
	}
	while(c>='0' && c<='9') {
		date=date*10+(c-'0');
		c=getchar();
	}
	return date*w;
}

double dist(int a,int b) {
	double ss=sqrt(abs(x[a]-x[b])*abs(x[a]-x[b])+abs(y[a]-y[b])*abs(y[a]-y[b]));
	return ss;
}

void dijkstra() {
	dis[s]=0.00;
	for(int i=1; i<=n; i++) {
		int t=-1;
		for(int j=1; j<=n; j++)
			if(!vis[j]&&(t==-1 || dis[j]<dis[t])) t=j;
		vis[t]=1;
		for(int j=1; j<=n; j++)
			if(dis[j]>dis[t]+f[t][j]) dis[j]=dis[t]+f[t][j];
	}
}

int main() {
	int h;
	n=read();
	for(int i=1; i<=n; i++)
		x[i]=read(),y[i]=read();
	m=read();
	memset(dis,INF,sizeof(dis));
	memset(f,INF,sizeof(f));
	for(int i=1; i<=m; i++) {
		int a=read(),b=read();
		double c=dist(a,b);
		f[b][a]=f[a][b]=c;
	}
	s=read(),h=read();
	dijkstra();
	printf("%.2f",dis[h]);
	return 0;
}

Dijkstra堆优化版

#include <bits/stdc++.h>
#define INF 0x7f7f7f7f
using namespace std;
typedef long long LL;
double dis[105],w[2500];
int e[2500],h[105],ne[2500];
int idx,n,m,s,t;
int vis[105],x[105],y[105];

struct node {
	int x;
	double d;
	bool operator < (node p) const {
		return d > p.d;
	}
	node(int x,double d):x(x),d(d) {}
};

inline int read() {
	int date=0,w=1;
	char c=getchar();
	while(c<'0' || c>'9') {
		if(c=='-') w=-1;
		c=getchar();
	}
	while(c>='0' && c<='9') {
		date=date*10+(c-'0');
		c=getchar();
	}
	return date*w;
}

void add(int a,int b,double c) {
	idx++;
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx;
}

void dijkstra() {
	priority_queue<node> q;
	memset(dis,INF,sizeof(dis));
	dis[s]=0;
	node u(s,dis[s]);
	q.push(u);
	while(!q.empty()) {
		node u=q.top();
		q.pop();
		if(vis[u.x]) continue; 
		vis[u.x]=1;
		for(int i=h[u.x]; i; i=ne[i]) {
			if(dis[e[i]] > dis[u.x] + w[i]) {
				dis[e[i]] = dis[u.x] + w[i];
				node v(e[i],dis[e[i]]);
				q.push(v);
			}
		}
	}
}

int main() {
	n=read();
	for(int i=1; i<=n; i++)
		x[i]=read(),y[i]=read();
	m=read();
	for(int i=1; i<=m; i++) {
		int a=read(),b=read();
		double c=dist(a,b);
		add(a,b,c);
		add(b,a,c);
	}
	s=read(),t=read();
	dijkstra();
	printf("%.2f",dis[t]);
	return 0;
}

bellman-ford算法

初始化dis为无穷大

dis[起点]=0

for i=1 to n-1

for j=1 to m

        判定是否符合松弛操作条件

        如果是则进行

for i=1 to m

        判定是否符合松弛操作条件

        如果是则有负权回路,否则输出答案

spfa(与dijkstra堆优化基本一样)

初始化dis为无穷大

定义一个存储int类型的队列

初始化dis[起点]=0

标记起点

将起点压入队列

队列非空

        取出队头元素并弹出

        vis[队头元素]=0

        for i=h[队头元素]; i是非空指针; i <- i的下一条边

                if dis[第i条边的后驱] <- dis[队头] + w[i]

                        dis[第i条边的后驱] <- dis[队头] + w[i]//松弛操作

                        if 当前边的后驱还未被标记

                                标记该后驱

                                将该后驱压入队列

floyd

初始化f为无穷大

for i=1 to n

f[i][i] <- 0

for k=1 to n

for i=1 to n

for j=1 to n

if f[i][j] > f[i][k] + f[k][j]

f[i][j] <- f[i][k] + f[k][j] 

补充

第二第四种方法需要用到邻接表,这里给出伪代码及模板

伪代码:

        构建邻接表的方式(idx用于表示当前为第idx条边):

        e[++idx] <- 当前边的后驱

        w[idx] <- 当前边的权值

        ne[idx] <- h[当前边的前驱]

        h[当前边的前驱] <- idx

模板:

void add(int a,int b,double c) {
	idx++;
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_谦言万语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值