P1216 ]数字三角形 (踢飞动规,最短路模板题)

题目大意:

洛谷题目传送门


解题思路:

很经典的一道动规题,各种算法教材都把这道题讲烂了,试问,学过动规的有几个没见过这道题?
虽然我一向很支持也很喜欢动规,但今天,我要一雪前耻,踢飞动规!

观察题目,使路径经过数字的和最大,裸裸的最短路标准提示语?如果不是教材,谁会想到这题要用动规呢?最短路水题啊!

这里简单阐述一下, 什么是最短路。

最短路分为两种,单源最短路和多源最短路。
单源最短路,就是说从一个起点到任一点的单个最短路径。
多源最短路,就是从任一点起点到任一点的多个最短路径。

简单来说,区别就在于对起点的规定,如果要求你每一个点都走一次最短路,有多个起点,那么就可以用多源最短路算法 Floyd 来做。如果只要求一个起点,那么就可以用 SPFA 算法Dijkstra 算法来做。

设某张图节点数为 n n n ,边数为 m m m,那么:
F l o y d Floyd Floyd 算法在多源最短路中的时间复杂度为 O ( n 3 ) O(n^3) O(n3),速度较慢,但处理多源最短路有良好的效果。

D i j k s t r a Dijkstra Dijkstra 算法在单源最短路中的时间复杂度 O ( n   l o g ( n + m ) ) O(n\ log(n+m)) O(n log(n+m)) 速度相对较快,不过由于算法的性质,仅适用于处理只包含正数的最短路,对负数无能为力。

S P F A SPFA SPFA 算法时间复杂度为 O ( n m ) O(nm) O(nm),比 D i j k s t r a Dijkstra Dijkstra 稍微逊色,不过代码量短,并且可以处理负数,甚至判负环!

SPFA算法时间复杂度较慢,但是用处广,所以这里大概的讲一下:

u u u 表示当前所在的节点, s s s 表示当前可以到达的某节点, a u , s a_{u,s} au,s 表示从点 u u u 到点 s s s 的边权, d i s u dis_u disu 表示从起点到 u u u 的最短路,现在我们要尝试更新 d i s s dis_s diss ,找到从起点到 s s s 的更优路径。
也就是说,如果 d i s u + a u , s   <   d i s s dis_u+a_{u,s}\ <\ dis_s disu+au,s < diss 的话,就用 d i s u + a u , s dis_u+a_{u,s} disu+au,s 更新 d i s s dis_s diss

因此有状态转移方程: d i s s = m i n   ( d i s u + a u , s   ,   d i s s ) dis_s=min\ (dis_u+a_{u,s}\ ,\ dis_s) diss=min (disu+au,s , diss)
举一个栗子:
先看一下这张图。
在这里插入图片描述
找到一条从节点 2 到节点 3 的最短路径,让我们来模拟一下SPFA算法流程。

初始化 d i s dis dis 数组,全部设为正无穷。
首先我们知道,直接与节点2相邻的节点有 3,1,因此从节点2出发,尝试更新其他节点的 d i s dis dis
我们尝试从节点2走到节点3,代价为3,比 d i s 3 dis_3 dis3 所存的代价要小,所以 d i s 3 = 3 dis_3=3 dis3=3,更新 d i s 3 dis_3 dis3
然后我们再从节点2走到节点1,代价为 -10,比 d i s 1 dis_1 dis1 小,因此更新 d i s 1 dis_1 dis1
然后我们从节点1出发,可以去到节点 3,S,4。
尝试从节点1走到节点3,其代价为 d i s 1 + 4 dis_1+4 dis1+4,由于 d i s 1 = dis_1= dis1=-10,加上4以后比 d i s 3 dis_3 dis3 要小,因此用 d i s 1 + 4 dis_1+4 dis1+4 更新 d i s 3 dis_3 dis3

在不能走回头路的情况下,很明显,从节点 2 走到节点 3 的最短路径长度是 -6,是按照这个路线走过来的: 2   − > 1   − > 3 2\ ->1\ ->3 2 >1 >3
但是,如果题目没有禁止走回头路的话,那么 SPFA算法就错了,因为,在节点 2 和节点1 出现了负数路径,如果我们在 2   − > 1   − > 2 2\ ->1\ ->2 2 >1 >2 这条路径反复横跳的话,那么路径长度会越来越小,SPFA就会出错。

但是还好,大部分良心题目都不会出现这种毒瘤情况,因为SPFA算法一般卡的是时间(到了那个时候建议用堆优化后的Dijkstra 算法),SPFA还是比较可靠的。(可惜他又死了)

切回正题,这题题目数据不大,行走的方向也只有两种,不会出现反复横跳的情况,因此我们可以考虑用 S P F A SPFA SPFA 算法(最短路和最长路其实就是一样的)。

用一个队列存起下一次展开探索的节点(标准的广搜思想),在每一次走的时候都尝试更新 d i s dis dis 数组,并判断松弛后的节点是否在队列中,如果不在则放入队列。


不一样的CODE:

#include <bits/stdc++.h>
using namespace std;
int R,a,ans=0;
int step[1001][1001];
int pictrue[1001][1001]={
    
    0};
struct Gar
{
    
    
	int x,y;
	Gar(int xx,int yy):x(xx),y(yy) {
    
    }  //X表示行,Y表示列,坐标结构体
};
int spfa(int sx,int sy)
{
    
    
	queue<Gar> q;
	bool visit[1001][1001]={
    
    false};  //数组判断当前松弛节点是否在队列中
	const int dx[3]={
    
    0,-1,-1},dy[3]={
    
    0,0,-1};  //事先存好题目中的两大方向
	q.push(Gar(sx,sy));
	step[sx][sy]=pictrue[sx][sy];
	while(!q.empty())
	  {
    
    
	  	Gar u=q.front();
	  	q.pop();  //取出节点尝试通过它来松弛
	  	if(u.x==R)
	  	  {
    
    
	  	  	ans=max(ans,step[u.x][u.y]);   //如果到底了,存起最优答案
	  	  	continue;
	  	  }
	  	visit[u.x][u.y]=true;  
	  	for(int i=1;i<=2;i++)
	  	  {
    
    
	  	  	int nx=u.x-dx[i],ny=u.y-dy[i];
	  	  	if(nx<=0||nx>R||ny<=0||ny>R) continue;   //判断是否超界
	  	  	if(step[nx][ny]<step[u.x][u.y]+pictrue[nx][ny])  //判断是否可以松弛
	  	  	  {
    
    
	  	  	  	  step[nx][ny]=step[u.x][u.y]+pictrue[nx][ny];   //STEP就是前文所述的DIS
				  if(!visit[nx][ny]) 
				    {
    
    
				    	q.push(Gar(nx,ny));   //如果不在队列就放入队列
				    	visit[nx][ny]=true;
				    }
	  	  	  }
	  	  }
	  }
}
int main()
{
    
    
	scanf("%d",&R);
	for(int i=1;i<=R;i++)
      for(int j=1;j<=i;j++)
	    {
    
    
	  	  cin>>a;
	  	  pictrue[i][j]=a;
	    }
	memset(step,-1,sizeof(step));  //由于是最大路,因此数组初始为负数
	ans=-1;
	spfa(1,1);
	printf("%d",ans);
	return 0;
} 

总结:

不难看出,由于为了 d i s dis dis 数组的最优性,一个图每个节点往往不止要访问一次,这就导致了SPFA在遇见稠密图的时候对时间的需求过高,很容易被卡掉 (所以他死了),在题目允许的情况下应该多避免使用 SPFA,毕竟,大多数最短路径问题都是可以用时间效率更高的 D i j k s t r a Dijkstra Dijkstra 来做。


总结的最后:

看到这里,或许有人会吐槽我,为什么一到这么水的题,有简短的DP不用,非得要浪费生命的打一个这么长的最短路代码。您当然可以这么觉得,但是我的博客只是为了更多的C++猿友看到算法的魅力,知道题目不仅仅有固定的配偶,让广大的C++爱好者发现原来算法和题目还能这么串联起来,那么我就不枉打这么多字来写这篇博客了。

能在鄙人这里得到一丁点启发的朋友们,欢迎留下你们的支持!!:)

如果你喜欢我的内容,那么也请支持一下他吧

Guess you like

Origin blog.csdn.net/SAI_2021/article/details/119902617