热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[除一波最短路的草]

以前感觉Dijkstra没jb用今天做了几道题感觉到了一点用处首先他是在处理密集的图的时候比Spfa会快一点时间是O(Nlogn)好像然后有一道题Spfa跑了一分钟Dijkstra0.1s忘

以前感觉Dijkstra没jb用 今天做了几道题感觉到了一点用处 首先他是在处理密集的图的时候比Spfa会快一点

时间是O(Nlogn)好像 然后有一道题Spfa跑了一分钟Dijkstra 0.1s 忘了什么题

所以Dijksta还是有点用的

 

其实在这里只想讲一点Dijkstra小的优化而已 并没有打算讲太多东西

1.用堆优化 sb都会写不会看白书(巫泽俊写的) P102-P103

2.其实这个优化不知道Spfa可不可以用 好像也可以,像往常一样 先给道例题 bzoj2200

这道题有单向边 有双向边 然后双向边是可以构成很多个联通块 单向边是连接联通块的单向边(无环)

 

直接草 抱歉超时 慢很多倍 那么我们可不可以考虑一个个联通块草 使得整个堆变小

 

这样的话我们先把双向边缩成联通块后 整个图就变成了DAG

然后按照拓扑序将联通块一块块合并起来 怎么说好呢 不算是合并 也就是一块块去做这个意思咯

对于单向边连的两块 影响就一个点 立即更新一下新联通块的点 但是很有可能这两块有很多个单向边连起来

然后再去做新的联通块 当然一开始有些联通块去不了的 你照样放进跑就好 反正也跑不了多少

但是有起点的联通块不一定入度不为0 所以的话 有起点的联通块 一定要从起点开始往外扫 不能从连入这个联通块的点扫(没意义)

#include
#include

#include

#include

#include

#include

#include

#define Maxn 26000
using namespace std;
struct node
{
int x,y,d,next;
}edge[Maxn
*10]; int len,first[Maxn];
void ins(int x,int y,int d)
{
len
++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;
}
int N,R,P,ST;

priority_queue
int,int>,vectorint,int> >,greaterint,int> > >Q;
queue
<int>S;

int dis[Maxn];

int fa[Maxn]; int Find(int x){if(x==fa[x]) return fa[x]; else return fa[x]=Find(fa[x]);}
int Hash[Maxn]; int cnt;

int X[Maxn*2],Y[Maxn*2],D[Maxn*2];
vector
<int>V[Maxn];
vector
<int>BL[Maxn];

int ru[Maxn];
int main()
{
freopen(
"roadplane.in","r",stdin);
freopen(
"roadplane.out","w",stdout);
scanf(
"%d%d%d%d",&N,&R,&P,&ST); len=0; memset(first,-1,sizeof(first));

for(int i=1;i<=N;i++) fa[i]=i;
for(int i=1;i<=R;i++)
{
int x,y,d; scanf("%d%d%d",&x,&y,&d); ins(x,y,d); ins(y,x,d);
fa[Find(x)]
=Find(y);
}
for(int i=1;i<=N;i++) if(fa[i]==i) Hash[i]=++cnt;
for(int i=1;i<=N;i++) fa[i]=Find(fa[i]);

for(int i=1;i<=P;i++)
{
scanf(
"%d%d%d",&X[i],&Y[i],&D[i]);
V[Hash[fa[X[i]]]].push_back(i); ru[Hash[fa[Y[i]]]]
++;

BL[Hash[fa[Y[i]]]].push_back(Y[i]);
}

memset(dis,
63,sizeof(dis)); dis[ST]=0; while(!S.empty()) S.pop();
for(int i=1;i<=cnt;i++) if(ru[i]==0) S.push(i);
for(int i=1;i<=N;i++) if(fa[i]==i&&ru[Hash[fa[i]]]==0) BL[Hash[fa[i]]].push_back(i);
BL[Hash[fa[ST]]].push_back(ST);
while(!S.empty())
{
int now=S.front(); S.pop();
for(int i=0;i)
{
int p=BL[now][i];
Q.push(make_pair(dis[BL[now][i]],BL[now][i]));
}
while(!Q.empty())
{
pair
<int,int> x=Q.top(); Q.pop();
if(dis[x.second]continue;
for(int k=first[x.second];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
if(dis[y]>dis[x.second]+edge[k].d)
{
dis[y]
=dis[x.second]+edge[k].d;
Q.push(make_pair(dis[y],y));
}
}
}

for(int i=0;i)
{
int p=V[now][i]; ru[Hash[fa[Y[V[now][i]]]]]--;

if(ru[Hash[fa[Y[V[now][i]]]]]==0) S.push(Hash[fa[Y[V[now][i]]]]);

dis[Y[V[now][i]]]
=min(dis[Y[V[now][i]]],dis[X[V[now][i]]]+D[V[now][i]]);
}
}


for(int i=1;i<=N;i++)
{
if(dis[i]<99999999) printf("%d\n",dis[i]);
else printf("NO PATH\n");
}
return 0;
}
/*
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
*/
View Code

 

然后还有一些题是最短路导航在线改边的(不是你们想象的在线 而是走一步路 最短路就更改)

这种题从终点扫过来就好 很难讲自己意会 例题bzoj 3538

 

此短路的话就多一个数组记录一下 上次的次短路加上这条边? 最短路被更新时是否次短路被更新? 最短路没被更新次短路会被更新?

想一下就好 我也是自己YY的 可能会想多了情况 我不负责 例题bzoj 1726

 

还有最近超热门的分层图 强烈要求分层图要用Dijkstra+Heap写 不然吃屎我不负责 例题bzoj 1579

 

求各点间 路径和 加上 路径上的点权的最大值?  Floyd简单O(N^4)可以写 就是枚举点权最大值 其实还有O(N^3)的写法

就是把点按照点权从小到大排 然后每一次k枚举的都是点权从小到大的点 那么你i到j最大值肯定是i或者j或者k 而以k作跳板 也就是F[i][k] F[k][j]这些路径中的点的最大值一定不会超过当前的点权(除两个端点除外) 例题bzoj 1774

 

还有一些反人类的floyd的题 边数比点数大... 就按边的两端的点离散化一下做就好了 其他都没意义 例题bzoj 1706

 

还有更反人类的 边数比点数大 单向边 然后还一定过某些点 求两点间最短距离之类的 一定过某些点的某些点个数不多 所以关于这些点的正的扫一遍 反着建边扫一遍 然后就知道这些点到其它点的距离 然后枚举中间点就好了 例题bzoj 4093

 

还有一些比较烦人的负环问题 白书上写的用Bellman-Ford来解决 其实我解决有两种办法

1.在SPFA上判断一个点有没有进入N次 这个很容易证 进入N次就代表有负环了

2.用DFS判断负环如果这个点DFS到一个已经进栈的点 那就有问题了

void Dfs(int x)
{
mark[x]
=true;
for(int k=first[x];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
if(dis[y]>=dis[x]+edge[k].d)
{
if(mark[y]){Flag=1; return ;}
else
{
dis[y]
=dis[x]+edge[k].d;
Dfs(y);
}
}
}
mark[x]
=false;
}

for(int i=1;i<=N;i++) {Dfs(i); if(Flag) return 1;}

这种方法好像比较快比较爽 但是还是习惯了用第一种

 

还有一些比较新的题 也就是分数规划 什么是分数规划 也就是说 题目要经过的点权和边权的比最小 那么就用到了

首先二分比值 然后把点权弄在边上 用比值乘于边权 也就是说现在的边权就是:(点权-边权*k) (自己写一写移项可得)

这样我们发现 当跑出来大于等于0是 k是合法的 不然不合法 但是我们很难判断是不是有合法 所以我们把边权取反 如果有一个负环 就证明有一条路是合法的

例题bzoj 1690

 

还有就是 其实最短路和差分约束系统是很相近的 他们有共同的东西 简单提一下

其实我们要化成A+C>=B (C是常数) 也就是说B比A最多大C 这样选最小的c 就是约束了

我只讲了点片面重点的 具体百度

 

A1-A0<=C 那么就可以移项变成 A0 -> A1 花费为C

A1-A0>=C A1->A0 花费为-C

然后画图 分类讨论一下就好了 例题bzoj 1731

 

再给一道比较妙的题 bzoj1698

spfa最短路径次数直接跑是不行的 原因有如下:

1.有1到1的情况 也就是先从一个1走到另一个1 再从另一个1走到1 代价不会变

所以这样的话我就想到了把荷叶变成联通块判断这种情况 但是还有..

2.打个比方:

0 0 y 0 0

1 0 0 0 1

0 0 x 0 0

x和y都是清水 从x去到y 可以经过左边的联通块去 可以经过右边的联通块去 答案有两种但其实就只有x,y放荷叶一种

这样处理想起来要解决其实很麻烦 所以我不会去看题解了

题解很简单 先想想 就是x到y所有路径之间都是1  但是不同的联通块 那么我们换位思考 那些不同的联通块连的荷叶点是不是都可以互相到达 然后花费为1

答案是的 所以的话 我们忽略联通块 就把联通块所能去到的荷叶点连起来就好了 但是这样连注意判重

其实下次看到这种题要想想那些不需要的可不可以删掉 来把图变得简单一点 

 

还有一些跨点建边的 也就是A->B B->C A可以到达C且免费 这样跑偷懒最多可以K次 这道题是亲身经历的 GDOI2015 Day2 第一题 当时随便草了个分成图 然后还上去讲了 最后只有30分..

就是因为边目录的时候不可以直接A->C  不然就会以C为跳板去到 A->C  C->D  A->D...

这个点 打边目录的孩子注意以下就好了 印象深刻 处理方法就是判断这一条边是不是新搞过出来的 其实还有一种特殊的建边方式 可以省边 但是我忘了 可以自己上网搜脑补

 

最后的话总结一下 就是判负环 DAG拓扑优化 任意两点边权加点权最小值或者最大值 分数规划 差分约束 分层图 在线导航 几乎就是这些了最短路

 

我说完了

如果你按照这份题表做完了 你会发现 我草我在带你刷bzoj水题 嘻嘻 所以刷完这份题表后记得评论一个 让我知道一下哪些人这么聪明跟我一起刷水题

有问题想题解仔细的也评论就好了

 


推荐阅读
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了C++中的引用运算符及其应用。引用运算符是一种将变量定义为另一个变量的引用变量的方式,在改变其中一个变量时,两者均会同步变化。引用变量来源于数学,在计算机语言中用于储存计算结果或表示值抽象概念。变量可以通过变量名访问,在指令式语言中引用变量通常是可变的,但在纯函数式语言中可能是不可变的。本文还介绍了引用变量的示例及验证,以及引用变量在函数形参中的应用。当定义的函数使用引用型形参时,函数调用时形参的改变会同时带来实参的改变。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
author-avatar
小老特
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有