ACM学习:最短路相关总结

本周主要学习了最短路的相关知识,28道题,大部分都有思路,在根据题解帮助排除误区后A了,一两道由于题目没懂没A。在解题过程中,更全面的了解了最短路径的四种算法。

一.例题解析

最短路径的题大多都是直接给出起点和终点,让我们根据所给数据组成最短路径。而根据题目的具体要求不同,一般用以下四种方法解答:

Dijstra算法:常用于单源最短路径的解决,是最常用的方法之一,但是,它虽然简单,却有一些弊端,因此在做题时,常常根据题目需求进行优化,常用的有堆优化等;(但是,有一个缺陷没法弥补,即无法计算有负权值的边)

例:
P1342 请柬 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=M4ADhttps://www.luogu.com.cn/problem/P1342题目要求:

这里的公交系统是非常特殊的:共有 nn 个站点和 mm 个线路,所有的线路都是单向的,连接两个站点。公共汽车离开起始点,到达目的地之后又空车返回起始点。

学生每天早上从总部所在的 11 号站点出发,乘公交车到一个预定的站点邀请乘客。每个站点都被安排了一名学生。在一天结束的时候,所有的学生都回到总部。现在需要知道的是,学生所需的公交费用的总和最小是多少。

分析:

这个是求权值的一道题,属于很常见的解题要求之一,实际上操作思想并不难,只要在进行最小路径寻找时,建立累加器加最短路径的权值即可。不过要记得在进行松弛操作时减去原来较长路径的权值,以防有误。(注意数据范围,一定要大,不要按习惯写int ,很容易越界)。

代码:

#include<bits/stdc++.h>
#include<iostream>
using namespace std;

const long long Maxn 2000065
#define next Next

long long read()
{
	long long p=0,f=1;char c=getchar();
	while((c<'0'||c>'9')&&(c!='-')) c=getchar();
	if(c=='-') f=-1,c=getchar();
	while(c>='0'&&c<='9') r=r*10+c-'0',c=getchar();
	return p*f;
}
priority_queue< pair<ll,ll> >q;
long long ans,n,m,tot,ver[Maxn],head[Maxn],next[Maxn],edge[Maxn],d[Maxn],v[Maxn];
void add(long long x,long long y,long long z)
{
	ver[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
	edge[tot]=z;
}
void dij(long long s)
{
	memset(d,0x7f,sizeof(d));
	memset(v,0,sizeof(v));
	d[s]=0;
	q.push(make_pair(0,s));
	while(q.size())
	{
		long long x=q.top().second;q.pop();
		if(v[x]) continue;
		v[x]=1;
		for(int i=head[x];i;i=next[i])
		{
			ll y=ver[i],z=edge[i];
			if(d[y]>d[x]+z)
			{
				d[y]=d[x]+z;
				q.push(make_pair(-d[y],y));
			}
		}
	}
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		long long x=read(),y=read(),z=read();
		add(x,y,z);
	}
	dij(1);
	for(int i=1;i<=n;i++) ans+=d[i];
	for(int i=2;i<=n;i++)
	{
		dij(i);
		ans+=d[1];
	}
	cout<<ans<<endl;
	return 0;
}

Floyd算法:适用于多源最短路径的题目,常用于求长度的题。算法思想有些类似动态规划,但实现起来比较方便。

例:

P1339 [USACO09OCT]Heat Wave G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=M4ADhttps://www.luogu.com.cn/problem/P1339题目要求:

有一个 nn 个点 mm 条边的无向图,请求出从 ss 到 tt 的最短路长度。

分析:

这是最基本的最短路径题,就是所谓的模板题,这个题的条件是无向图,就是不断寻找当前的点与连接点的最小路径。

思路:

先把每个都储存为无穷大(可以用INF来建立,方便看懂),然后利用正常思路解题,但是注意,因为是无向图,要两遍松弛。(思路来自题解)

代码:

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int MAXN = 6200 + 5;
const int INF = 2e9;
int u[MAXN], w[MAXN], v[MAXN], dis[2501], s, e, m, n;
bool t;
int main()
{
    cin >> m >> n >> s >> e;
    for (int i = 1; i <= n; i++)
        cin >> u[i] >> v[i] >> w[i];
    for (int i = 1; i <= m; i++)
        dis[i] = INF;
    dis[s] = 0;
    for (int j = 1; j <= m-1; j++)
    {
        t = true;
        for (int i = 1; i <= n; i++)//就是这,松弛操作两向
        {
            if (dis[v[i]] > dis[u[i]] + w[i])
            {
                dis[v[i]] = dis[u[i]] + w[i];
                t = false;
            }
            if (dis[u[i]] > dis[v[i]] + w[i])
            {
                dis[u[i]] = dis[v[i]] + w[i];
                t = false;
            }
        }
        if (t) 
        break;
    }
    cout << dis[e];
}

Bellman ford算法:也是单源最短路径的主要使用方法,一般适用于有负权值的边的题;

SPFA算法:有非常强的适配性,可以很好的联合搜索,stl等方法进行解题,适用于单源多源等很多题目,广泛性高,解决能力强。

由于我多用floyd算法解题,对后面两种只是看懂会用,就不专门列例题解答。接下来主要谈谈心得。而且实际上,这很多题都是可以利用其他思路解决,比如在本周的题集中,我就看到了一些过往做过的题,其实,一些题利用不同的方法确实可以有不同的解题思路,明显的如并查集和搜索,但最短路径作为最小生成树的相交区域,也涉及到了二叉树的一些知识,而且有的题联系了一些未学过的知识,也就只能浅尝辄止。

二.解题心得

本周写题时,发现最短路径的有关题目同质化较高,都是在不同条件下用同样的方法解同样的题,然而,简单的确实一看就有思路,但难的确实很难下手,比如P6464 传送门 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题,我一开始的思路很简单,就是简单的dij去反复解,但看了题解才按照过程一步步明白了题目设置的坑,很可惜的是,最后只是过了案例,题目过不去,百思不得其解(最后改的案例都过不去了),这说明我的代码能力还有待提高。还有就是,有的题目依旧需要利用反向思维。对于没头绪的可以更深一步思考。

当然,本周收获也不能说是满打满的。对于SPFA算法,虽然绝大部分的题都有这种算法的解法,但我确实不是很熟练,所以在下周复习时要重点看一看。

本周博客确实不知应当如何具体去描述心路历程,很多想法都已经变成题解放在excle里了,而且本周主要就是看题,导致本周的博客可能有些寡淡,这周就先这样吧。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值