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 ...
随机推荐
- @b.windows.last.use
@b.windows.last.use @b.windows.first.use be_true 一般用在step文件中
- 《大话设计模式》num01---简单工厂模式
2017年12月10日 20:13:57 独行侠的守望 阅读数:128更多个人分类: 设计模式编辑版权声明:本文为博主原创文章,转载请注明文章链接. https://blog.csdn.net/xia ...
- hibernate课程 初探单表映射3-5 hibernate增删改查
本节简介: 1 增删改查写法 2 查询load和查询get方法的区别 3 demo 1 增删改查写法 增加 session.save() 修改 session.update() 删除 session. ...
- 系统整理 精讲 swift 泛型
泛型是一种非常领会的语法,让我很是膜拜! 真是让人又爱又恨,学不懂的时候很抓狂 允许程序在函数,枚举,结构体,类中定义类型形参(类型可以动态改变) 每次使用可以传入不同类型的形参! Array< ...
- 彻底解决Android 应用方法数不能超过65K的问题
作为一名Android开发者,相信你对Android方法数不能超过65K的限制应该有所耳闻,随着应用程序功能不断的丰富,总有一天你会遇到一个异常: Conversion to Dalvik forma ...
- Elasticsearch-2.3 (OpenLogic CentOS 7.2)
平台: CentOS 类型: 虚拟机镜像 软件包: elasticsearch-2.3 application server basic software big data elasticsearch ...
- 【BZOJ1257】[CQOI2007] 余数之和(数学题)
点此看题面 大致题意: 求\(\sum_{i=1}^nk\%i\). 关于除法分块 这是一道除法分块的简单应用题. 式子转换 显然\(k\%i\)是一个很难处理的项. 于是我们就要使用使用一个常用的套 ...
- 2018.2.2 java中的Date如何获取 年月日时分秒
package com.util; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; pub ...
- 机器学习_线性回归和逻辑回归_案例实战:Python实现逻辑回归与梯度下降策略_项目实战:使用逻辑回归判断信用卡欺诈检测
线性回归: 注:为偏置项,这一项的x的值假设为[1,1,1,1,1....] 注:为使似然函数越大,则需要最小二乘法函数越小越好 线性回归中为什么选用平方和作为误差函数?假设模型结果与测量值 误差满足 ...
- appium---adb通过wifi连接手机
前几天接到领导的安排,想要测试下apk的耗电量,可以通过手机adb命令进行监控手机电量的变化:但是这样如果通过USB连接手机的话,USB就会自动给手机进行充电,无法达到我们想要的结果,于是想到了通过w ...