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 ...
随机推荐
- 小试OKR一季度之后有感分享,你要不要试试ORK?
封面 OKR已经在国内热火朝天有一阵子了,为了适当的赶时髦,从年初开始团队内部小范围使用ORK模式以便测试团队会有什么化学反应.这篇文章打算写写心得感受,供大家围观产考. 老一套先摆一下概念 OKR( ...
- c++11 多线程入门教程(一)
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/10945309.html 最近在找c++服务端开发的实习(大佬们有推荐吗QAQ..),恰好写了一 ...
- SOA框架
SOA是什么 估计很多人都听说过SOA这个词了,但是很多人还是不知道到底什么是SOA.开发人员很容易理解为是一个Web Service,但是这绝对不是SOA,那顶多只能算是SOA的一种实现方法.那么, ...
- 操作文件方法简单总结(File,Directory,StreamReader,StreamWrite )(转载)
本文转自http://www.cnblogs.com/zery/p/3315889.html 对于文件夹,文档的操作一直处于一知半解状态,有时间闲下来了,好好练习了一把,对文档,文件的操作有了一个基本 ...
- python.h没有那个文件或目录解决方法
我用的是Deepin Linux,这应该是linux平台的问题,别的linux os也是执行安装,命令不同而已,windows和Mac不太清楚. 如果你使用的是python2.x,那么使用下面的语句: ...
- Python __builtin__模块
你有没有好奇过当我们打开Python后就可以直接使用str(),list(),eval(),print(),max()这样的函数,而不用导入任何模块? 其实原因很简单,就是当我们打开Python解释器 ...
- Excel2Dataset
//获取用户打开的Excel文档路径 private stringkkk() { OpenFileDialog selectFile = new OpenFileDialog(); selectFil ...
- LeetCode OJ Container With Most Water 容器的最大装水量
题意:在坐标轴的x轴上的0,1,2,3,4....n处有n+1块木板,长度不一,任两块加上x轴即可构成一个容器,其装水面积为两板的间距与较短板长之积,以vector容器给出一系列值,分别代表在0,1, ...
- 笔记 Activator.CreateInstance(Type)
这段代码取自NopCommerce 3.80 的 权限列表初始化代码 dynamic provider = Activator.CreateInstance(providerType); 文件位置 ...
- Codeforces 744A. Hongcow Builds A Nation
A. Hongcow Builds A Nation 题意: 现在有 n 个点 ,m 条边组成了一个无向图 , 其中有 k 个特殊点, 这些特殊点之间不能连通 ,问可以再多加几条边? 因为$x^2+y ...