dijkstra与他的优化!!!
@
SPFA已死,有事烧纸
其实我本人也是一个SPFA的忠诚用户,至少我的最少费用最大流都是用SPFA打的。但是,就在最近,发生了一个天大的丑闻!一个大佬竟将SPFA卡死!!!还有千千万万的SPFA站起来!!!
不过SPFA其实还是可以用来拿一定的分数,比如一些很神奇的优化。
这时,我们的\(dijkstra\)便一下子火了!
而且\(dijkstra\)也可以加优化,最快可以到达\(O(nlogn+m)\),吊打SPFA?
Dijkstra
注:Dijkstra不能处理负边的情况。

首先,我们有四个点,1号点的点权为0,那么我们就用他更新其他的点。

那么现在在白色点里面,4号点是最小的,我们继续用4号点更新。

继续:

那么我们就可以得到一段代码。
bool bk[21000];
int d[21000];
d[0]=inf;
for(int i=1;i<n;i++)
{
int id=0;
for(int i=1;i<=n;i++)
{
if(!bk[i] && d[i]<d[id])id=i;
}//找最小
bk[id]=true;
for(int k=last[id];k;k=a[k].next)
{
int c=a[k].c;//边权
if(d[a[k].y/*边的另外一个终点*/]>d[id]+c)d[a[k].y]=d[id]+c;
}
}
当然,这种做法也是显而易见的正确了,在无负边的情况下当前距离最短的没被选过的点肯定不能被其他没被选过的点更新,实则就是加了贪心思想。
但是因为裸写的复杂度为\(O(n^{2}+m)\),在找最小值方面较慢,所以也不行,需要用数据结构优化。
配对堆
引言
这里的配对堆不是pb_ds里面的!!!!!!!!!!!!!!!!!!!!
在无尽地堆海世界里面,有个叫斐波纳契堆的玩意,修改插入\(O(1)\),弹出堆顶\(O(logn)\),而二项堆则较为鸡肋,而这两个常数都大的一匹(代码也长)!!!
而二叉堆、优先队列(STL,开O2的话配对堆的时间和他差不了多少)、zkw线段树(不是堆,但这个常数真的小,配对堆跟他比还是慢一点),但是由于修改为\(O(logn)\),导致时间复杂度变为\(O(n+mlogn)\)(优先队列的一般写法是\(O(n+logm)\),因为不支持修改),但是常数小。
有没有综合一点的一个数据结构呢?
这是,配对堆便闪闪发光了,因为代码简洁和具备斐波纳契堆一样的复杂度,以及常数较小,被世人所赞叹,不过,还是存在许多不确定性的,毕竟没有完美的事物。
讲解
配对堆实质上是一棵树,用边目录进行连接
由于这个写的是排序,所以就比较简单,但是在优化Dij时因为要修改,为防止内存大和速度快,改了一些内容,当然,等你学会了排序后,这个差不多就会了。
下面讲
合并
两个堆的堆头,哪个小哪个为根,另外一个认他为父亲,就这么粗暴!!!
inline int merge(int x,int y){v[x]>v[y]?(x^=y^=x^=y):0;inz(x,y);return x;}
修改
就是我与父亲断绝关系,然后我修改我自己的值,将我与我的子树与root合并。
按我的理解是\(O(1)\),而且对后面\(pop\)的影响也只是\(1\)或\(2\)次的影响,不知道为什么官方说是\(O(logn)\),当然,此篇文章将按\(O(1)\)来算。
当然,这样只能改小,不能改大,改大的话可以断绝关系后pop一下然后再合并回来,\(O(logn)\)的时间。
void change(int id,LL x){v[id]=x;fa[id]=0;root=(root==id?id:merge(root,id));/*这条边就浪费了*/}//改变边权,这题不用
当然,边就浪费了,在后面会有一个回收的版本,但是慢一点,内存会小。
弹出堆顶pop
这么随便的一个数据结构为什么复杂度这么优秀,全看核心操作,pop(),将堆顶的所有儿子按一定顺序合并起来,然后弹出堆顶,而合并的顺序也是有讲究的,不是一个接一个,而是两两合并,这样每个点的儿子最多只有\(logn\)个,而配对堆时间复杂度最优秀的时候则是一条链的时候,排序都排序好了!
给个图好理解:

不是树,是合并的过程。
这个时候也许我们的儿子合并代码会这样写:
//vip为list中点的数量,list为儿子
int p=vip/2;
while(vip>1)
{
for(int i=1;i<=p;i++)list[i]=merge(list[i],list[i+p]);
(vip&1)?list[++p]=list[vip]:1;vip=p;p=vip/2;
}
但是大佬强就强在了这里(可以点击):
int p=1;
while(p<vip)list[++vip]=merge(list[p],list[p+1]),p+=2;
简单,明了!!!!
那么给出pop完整的代码:
int list[M],vip;
inline void pop()
{
vip=0;
for(int k=las[root];k;k=e[k].next)
{
zjj.save(k)/*回收边的编号*/,(fa[e[k].y]==root?(be[list[++vip]=e[k].y]=fa[e[k].y]=0):0);
}
int p=1;
while(p<vip)
{
list[++vip]=merge(list[p],list[p+1]),p+=2;
}
las[root]=0;root=list[vip];
}
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define N 110000
using namespace std;
typedef long long LL;
class dui
{
public:
struct neicun
{
int a[N],ts,len;
neicun(){ts=0;len=0;}
int get(){return ts?a[ts--]:++len;}
void save(int id){a[++ts]=id;}//存边
}zjj;//内存池
int root,fa[N]/*父亲*/;
LL v[N];//权值
struct node
{
int y,next;
}a[N];int len,last[N];
void ins(int x,int y){int now=zjj.get();a[now].y=y;a[now].next=last[x];last[x]=now;fa[y]=x;}//建边
int merge(int x,int y){v[x]>v[y]?(x^=y^=x^=y):0;ins(x,y);return x;}//合并
void push(int id,LL x){v[id]=x;root=(root?merge(root,id):id);}//推入堆中
void change(int id,LL x){v[id]=x;fa[id]=0;root=(root==id?id:merge(root,id));/*这条边就浪费了*/}//改变边权,这题不用
LL top(){return v[root];}
int list[210000],vip;
void pop()//踢出堆顶
{
vip=0;
for(int k=last[root];k;k=a[k].next)zjj.save(k),(fa[a[k].y]==root?fa[list[++vip]=a[k].y]=0:0);
int p=1;
while(p<vip)list[++vip]=merge(list[p],list[p+1]),p+=2;
last[root]=0;root=list[vip];
}
}zs;
int n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
LL x;scanf("%lld",&x);
zs.push(i,x);
}
for(int i=1;i<n;i++)
{
printf("%lld ",zs.top());
zs.pop();
}
printf("%lld\n",zs.top());
return 0;
}
结合!
1
那么,这道题目看着很简单,其实很恶心,因为如果你堆里面的边目录开到10000000,内存就炸裂,所以必须要将change的边回收,同时,一开始不要建所有的点,都是一些比较重要的细节。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define N 1000002
#define NN 2000002
#define M 10000002
using namespace std;
typedef long long LL;
//堆
bool type[N];
struct neicun
{
int qaq[N],ts,len;
neicun(){ts=0;len=0;}
int get(){return ts?qaq[ts--]:++len;}
void save(int id){qaq[++ts]=id;}
}zjj;//内存
int root;
LL v[N];
struct node
{
int x,y,next,step;//双向链表
}e[N];int las[N],be[N]/*自己连向父亲的那条边*/,fa[N];
inline void inz(int x,int y){int now=zjj.get();e[now].x=x;e[now].y=y;e[now].next=las[x];e[now].step=0;e[las[x]].step=now;las[x]=be[y]=now;fa[y]=x;}
inline int merge(int x,int y){v[x]>v[y]?(x^=y^=x^=y):0;inz(x,y);return x;}
inline void push(int id,LL x){v[id]=x;root=(root?merge(root,id):id);}
inline void put(int id){e[e[id].next].step=e[id].step;e[e[id].step].next=e[id].next;}//删点
inline void change(int id,LL x)
{
if(!type[id])
{
type[id]=true;
push(id,x);
return ;
}//一开始不要提前建所有的点
v[id]=x;
if(be[id]){put(be[id]);be[id]==las[fa[id]]?las[fa[id]]=e[be[id]].next:0;zjj.save(be[id]);fa[id]=be[id]=0;}
root=(root==id?id:merge(root,id));
}
inline int top(){return root;}
int list[NN],vip;
inline void pop()
{
vip=0;
for(int k=las[root];k;k=e[k].next)
{
zjj.save(k),(fa[e[k].y]==root?(be[list[++vip]=e[k].y]=fa[e[k].y]=0):0);
}
int p=1;
while(p<vip)
{
list[++vip]=merge(list[p],list[p+1]),p+=2;
}
las[root]=0;root=list[vip];
}
//
struct bian
{
int y,next,c;
}a[M];int len,last[N];//边目录
inline void ins(int x,int y,int c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
int n,m;
int main()
{
scanf("%d%d",&n,&m);
int o1;LL o2,o3,o4,o5,o6;scanf("%d%lld%lld%lld%lld%lld",&o1,&o2,&o3,&o4,&o5,&o6);
LL x=0,y=0;//建边
for(int i=1;i<=o1;i++)
{
x=(x*o2+o3)%o6;y=(y*o4+o5)%o6;
int a=int(x%(LL)n+1),b=int(y%LL(n)+1);
if(a>b)a^=b^=a^=b;
ins(a,b,1e8-100*a);
}//
for(int i=m-o1;i>=1;i--)
{
int x,y,c;scanf("%d%d%d",&x,&y,&c);
ins(x,y,c);
}
for(int i=1;i<=n;i++)v[i]=LL(99999999999999999);
change(1,0);
for(int i=1;i<n;i++)
{
int id=top();pop();
for(int k=last[id];k;k=a[k].next)
{
LL c=a[k].c;
if(v[a[k].y]>v[id]+c)change(a[k].y,v[id]+c);
}
}
printf("%lld\n",v[n]);
return 0;
}
虽然速度慢了点。
同时,这道题数据很水。
LL x=0,y=0;//建边
for(int i=1;i<=o1;i++)
{
x=(x*o2+o3)%o6;y=(y*o4+o5)%o6;
int a=int(x%(LL)n+1),b=int(y%LL(n)+1);
if(a>b)a^=b^=a^=b;
ins(a,b,1e8-100*a);
}
这一段建边可以不用,也可以A,同时你的速度也会快几十秒!!!
QAQ。
2
这道题目就很简单了吗!
// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define N 100005
#define M 200005
using namespace std;
typedef long long LL;
bool type[N];
struct neicun
{
int qaq[N],ts,len;
neicun(){ts=0;len=0;}
int get(){return ts?qaq[ts--]:++len;}
void save(int id){qaq[++ts]=id;}
}zjj;
int root;
LL v[N];
struct node
{
int x,y,next,step;
}e[N];int las[N],be[N],fa[N];
inline void inz(int x,int y){int now=zjj.get();e[now].x=x;e[now].y=y;e[now].next=las[x];e[now].step=0;e[las[x]].step=now;las[x]=be[y]=now;fa[y]=x;}
inline int merge(int x,int y){v[x]>v[y]?(x^=y^=x^=y):0;inz(x,y);return x;}
inline void push(int id,LL x){v[id]=x;root=(root?merge(root,id):id);}
inline void put(int id){e[e[id].next].step=e[id].step;e[e[id].step].next=e[id].next;}
inline void change(int id,LL x)
{
if(!type[id])
{
type[id]=true;
push(id,x);
return ;
}
v[id]=x;
if(be[id]){put(be[id]);be[id]==las[fa[id]]?las[fa[id]]=e[be[id]].next:0;zjj.save(be[id]);fa[id]=be[id]=0;}
root=(root==id?id:merge(root,id));
}
inline int top(){return root;}
int list[M],vip;
inline void pop()
{
vip=0;
for(int k=las[root];k;k=e[k].next)
{
zjj.save(k),(fa[e[k].y]==root?(be[list[++vip]=e[k].y]=fa[e[k].y]=0):0);
}
int p=1;
while(p<vip)
{
list[++vip]=merge(list[p],list[p+1]),p+=2;
}
las[root]=0;root=list[vip];
}
struct bian
{
int y,next;
LL c;
}a[M];int len,last[N];
inline void ins(int x,int y,LL c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
int n,m,st;
LL d[N];
int main()
{
scanf("%d%d%d",&n,&m,&st);
for(int i=1;i<=m;i++)
{
int x,y;LL c;scanf("%d%d%lld",&x,&y,&c);
ins(x,y,c);
}
for(int i=1;i<=n;i++)d[i]=LL(99999999999999999);
d[st]=0;change(st,0);
for(int i=1;i<n;i++)
{
int id=top();
pop();
for(int k=last[id];k;k=a[k].next)
{
if(d[a[k].y]>d[id]+a[k].c)
{
d[a[k].y]=d[id]+a[k].c;
change(a[k].y,d[a[k].y]);
}
}
}
for(int i=1;i<n;i++)printf("%lld ",d[i]);
printf("%lld\n",d[n]);
return 0;
}
dijkstra与他的优化!!!的更多相关文章
- Til the Cows Come Home 最短路Dijkstra+bellman(普通+优化)
Til the Cows Come Home 最短路Dijkstra+bellman(普通+优化) 贝西在田里,想在农夫约翰叫醒她早上挤奶之前回到谷仓尽可能多地睡一觉.贝西需要她的美梦,所以她想尽快回 ...
- 关于dijkstra的小根堆优化
YY引言 在NOI2018D1T1中出现了一些很震惊的情况,D1T1可以用最短路解决,但是大部分人都在用熟知的SPFA求解最短路.而SPFA的最坏复杂度能够被卡到$O(VE)$.就是边的数量乘以点的数 ...
- dijkstra算法的堆优化
普通的dijkstra算法模板: //数据结构 int g[LEN][LEN]; //邻接矩阵 int vis[LEN]; //标记是否访问 int dist[LEN] //源点到各点的距离 fill ...
- dijkstra算法之优先队列优化
github地址:https://github.com/muzhailong/dijkstra-PriorityQueue 1.题目 分析与解题思路 dijkstra算法是典型的用来解决单源最短路径的 ...
- 单源最短路径:Dijkstra算法(堆优化)
前言:趁着对Dijkstra还有点印象,赶快写一篇笔记. 注意:本文章面向已有Dijkstra算法基础的童鞋. 简介 单源最短路径,在我的理解里就是求从一个源点(起点)到其它点的最短路径的长度. 当然 ...
- 单源最短路-dijkstra算法(未优化)
bool used[maxn]; int g[maxn][maxn]; // 边未联系的填充为INF int d[maxn]; void dijkstra(int s){ memset(g,false ...
- 【Luogu P4779】dijkstra算法的堆优化
Luogu P4779 利用堆/优先队列快速取得权值最小的点. 在稠密图中的表现比SPFA要优秀. #include<iostream> #include<cstdio> #i ...
- POJ-2387.Til the Cows Come Home.(五种方法:Dijkstra + Dijkstra堆优化 + Bellman-Ford + SPFA + Floyd-Warshall)
昨天刚学习完最短路的算法,今天开始练题发现我是真的菜呀,居然能忘记邻接表是怎么写的,真的是菜的真实...... 为了弥补自己的菜,我决定这道题我就要用五种办法写出,并在Dijkstra算法堆优化中另外 ...
- 手写堆优化dijkstra
\(dijkstra\) 算法的堆优化,时间复杂度为\(O(n+m)\log n\) 添加数组\(id[]\)记录某节点在堆中的位置,可以避免重复入堆从而减小常数 而这一方法需要依托手写堆 #incl ...
随机推荐
- Spring注解之@Lazy注解,源码分析和总结
一 关于延迟加载的问题,有次和大神讨论他会不会直接或间接影响其他类.spring的好处就是文档都在代码里,网上百度大多是无用功. 不如,直接看源码.所以把当时源码分析的思路丢上来一波. 二 源码分析 ...
- 批量 多个JPG生产PDF .net C#
using iTextSharp.text; using iTextSharp.text.pdf; using System; using System.Collections.Generic; us ...
- Response.Redirect 打开新窗口的两种方法
一般情况下,Response.Redirect 方法是在服务器端进行转向,因此,除非使用 Response.Write("<script>window.location='htt ...
- 现代java开发指南系列
[翻译]现代java开发指南系列 [翻译]现代java开发指南 第一部分 [翻译]现代java开发指南 第二部分 [翻译]现代java开发指南 第三部分
- ORACLE将查询的多条语句拼在一个字段下
select listagg(字段名,'分隔符') within group (order by 某个字段)
- 阿里云部署安装redis无法访问
ps:我在linux上安装redis后发现一直端口不通连接不上,折腾一晚上.后来在阿里云官方回复(机器人)中看到下面的回复: 2019/02/28 22:50 自己一试,过完是端口监听是本 ...
- A promise tomorrow is worth a lot less than trying today.
A promise tomorrow is worth a lot less than trying today.明日的承诺远不及今日的行动.
- java集合杂谈
一:java集合框架如下图所示: 大致说明:看上面的框架图,先抓住它的主干,即Collection和Map. 1.Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性.C ...
- 菜鸟 学注册机编写之 “RSA”
测试环境 系统: xp sp3 调试器 :od 1.10 RSA简单介绍 选取两个别人不知道的大素数p, q. 公共模n = p*q 欧拉值φ(n) = (p-1)(q-1) 选取公匙(加密匙) e ...
- vue-绑定style、css
class.style的绑定1.在 v-bind 用于 class 和 style 时, Vue.js 专门增强了它.表达式的结果类型除了字符串之外,还可以是对象或数组2.绑定css2.1对象绑定2. ...