背景
防止赛场上面被卡SPFA,且效率更高更稳定
测试
测试了STL的,和pbds的优先队列,在Dijkstra堆优化中谁更快
- 对照实验,除了用的容器不同,其余代码段全部一致,保证了只受到STL和pbds影响,都不开O2,都用了快读,用了vector邻接表。
- 记录运行时间的上界和下界
- 题目链接:洛谷P4779 【模板】单源最短路径(标准版)
STL,初始堆中只有一个元素(起点):141-252ms
STL,初始堆中有n个元素:238-384ms
- pbds,初始堆中只有一个元素(起点):62-110ms
- pbds,初始堆中有n个元素:75-130ms
手写堆,初始堆中有所有元素:39-69ms
此处手写堆就没有对照实验了,手写堆还是快的离谱hhh
- 综上测试,使用pbds里面可修改的优先队列,速度还是比STL里面快的,且初始时,堆中仅有一个起点的时候更快。
- 但是如果没有修改和合并的情况,一般还是STL里的堆更快
代码实现
pb_ds写法
- pbds,初始堆中只有一个元素(起点)
#include <bits/stdc++.h>
#include <bits/extc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
#define debug(x) cerr << #x << ": " << x << '\n'
#define bd cerr << "----------------------" << el
#define el '\n'
#define cl putchar('\n')
#define pb push_back
#define eb emplace_back
#define x first
#define y second
#define rep(i, a, b) for (register int i = (a); i <= (b); i++)
#define loop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
#define ceil(a, b) (a + (b - 1)) / b
#define ms(a, x) memset(a, x, sizeof(a))
#define INF 0x3f3f3f3f
#define db double
typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<db, db> PDD;
typedef vector<int> vci;
typedef map<int, int> mii;
typedef mii::iterator mii_it;
const int N = 1e5 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7;
const double PI = acos(-1), eps = 1e-8;
int T, n, m;
#define param PII, greater<PII>, pairing_heap_tag
__gnu_pbds::priority_queue<param> q;
__gnu_pbds::priority_queue<param>::point_iterator its[N];
int dis[N];
vector<PII> g[N];
inline int read()
{
int f = 1, d = 0;
char ch;
ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
d = (d << 1) + (d << 3) + (ch ^ 48), ch = getchar();
return d * f;
}
void dijkstra(int sta)
{
q.clear();
for (int i = 1; i <= n; i++)
{
dis[i] = INF; //堆根据first排序,所以dis要放在前面,id放在后面
}
its[sta] = q.push({0, sta});
dis[sta] = 0;
int u;
#define v first
#define w second
while (!q.empty())
{
u = q.top().second;
q.pop();
for (auto e : g[u])
{
if (dis[e.v] > dis[u] + e.w)
{
dis[e.v] = dis[u] + e.w;
if(its[e.v]!=NULL)q.modify(its[e.v], {dis[e.v], e.v});
else {
its[e.v] = q.push({dis[e.v], e.v});
}
}
}
}
#undef v
#undef w
return;
}
int main()
{
cin.tie(0);
cout.tie(0);
cin.sync_with_stdio(false);
int s;
// cin >> n >> m >> s;
n = read();
m = read();
s = read();
int u, v, w;
rep(i, 1, m)
{
// cin >> u >> v >> w;
u = read();
v = read();
w = read();
g[u].eb(v, w);
}
dijkstra(s);
rep(i, 1, n) if (dis[i] != INF) cout << dis[i] << ' ';
else cout << INT_MAX << ' ';
}
STL写法
#include <bits/stdc++.h>
#include <bits/extc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
#define debug(x) cerr << #x << ": " << x << '\n'
#define bd cerr << "----------------------" << el
#define el '\n'
#define cl putchar('\n')
#define pb push_back
#define eb emplace_back
#define x first
#define y second
#define rep(i, a, b) for (register int i = (a); i <= (b); i++)
#define loop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
#define ceil(a, b) (a + (b - 1)) / b
#define ms(a, x) memset(a, x, sizeof(a))
#define INF 0x3f3f3f3f
#define db double
typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<db, db> PDD;
typedef vector<int> vci;
typedef map<int, int> mii;
typedef mii::iterator mii_it;
const int N = 1e5 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7;
const double PI = acos(-1), eps = 1e-8;
int T, n, m;
std::priority_queue<PII, vector<PII>, greater<PII>> q;
int dis[N];
bool vis[N];
vector<PII> g[N];
inline int read()
{
int f = 1, d = 0;
char ch;
ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
d = (d << 1) + (d << 3) + (ch ^ 48), ch = getchar();
return d * f;
}
void dijkstra(int sta)
{
for (int i = 1; i <= n; i++)
{
dis[i] = INF; //堆根据first排序,所以dis要放在前面,id放在后面
if(i != sta) q.push({INF, i});
else q.push({0, sta});
}
dis[sta] = 0;
// q.push({0, sta});
int u;
while (!q.empty())
{
u = q.top().second;
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (auto e : g[u])
{
if (dis[e.first] > dis[u] + e.second)
{
dis[e.first] = dis[u] + e.second;
q.push({dis[e.first], e.first});
}
}
}
return;
}
int main()
{
cin.tie(0);
cout.tie(0);
cin.sync_with_stdio(false);
int s;
// cin >> n >> m >> s;
n = read();
m = read();
s = read();
int u, v, w;
rep(i, 1, m)
{
// cin >> u >> v >> w;
u = read();
v = read();
w = read();
g[u].eb(v, w);
}
dijkstra(s);
rep(i, 1, n) if (dis[i] != INF) cout << dis[i] << ' ';
else cout << INT_MAX << ' ';
}
手写堆
- 手写堆,初始堆内有n个元素
#include<bits/stdc++.h>
using namespace std;
const int mx=1e5+10;
const int mn=1e6+10;
const int Max=0x3f3f3f3f;
struct Graph {
int to,nex,w;
} e[mn];
int last[mx],tot;
void addedge(int from,int to,int w) {
e[++tot].to=to;
e[tot].nex=last[from];
e[tot].w=w;
last[from]=tot;
}
int n,m,sta,des,cnt,pos[mx],Hsize,pre[mx];
bool s[mx];
//最小堆
struct dui{
int d,id;
//d代表dis距离
}Heap[mx];
//Heap[0]是中间变量
//Heap[1]是堆顶
void swap(int a,int b){
//pos代表 编号在堆中的位置,Heap代表堆中所储存的信息(距离和编号)
Heap[0]=Heap[a];
Heap[a]=Heap[b];
Heap[b]=Heap[0];
//里面储存的其实已经是b的信息了
pos[Heap[a].id]=a;
pos[Heap[b].id]=b;
}
void Up(int i){//上浮
while((i != 1) &&( Heap[i].d < Heap[i>>1].d )){
swap(i,i >> 1);
i >>= 1;
}
}
void Delete(){
swap(1,Hsize);//删除堆顶
Hsize--;
//下沉
int Par,Chi;
for(Par=1;Par << 1 <=Hsize;Par=Chi){
Chi=Par << 1;
if(Chi+1<=Hsize&&Heap[Chi].d>Heap[Chi+1].d)Chi++;//选择出来,比较小的那个儿子
if(Heap[Par].d<Heap[Chi].d)break;//如果父亲已经比两个儿子都小,则合法,break
swap(Par,Chi);//交换二者
}
}
void Relax(int u,int v,int w){//放松w边
if(w + Heap[pos[u]].d<Heap[pos[v]].d){
Heap[pos[v]].d=w + Heap[pos[u]].d;
pre[v]=u;//v的前驱是u
Up(pos[v]);//上浮,跟父亲比较
}
}
void dijkstra(){
for(int i=1;i<=n;i++){
Heap[i].id=pos[i]=i;
Heap[i].d=Max;//赋最大值
}
Heap[sta].d=0;
swap(1,sta);//起点弄到堆顶
Hsize=n;
int u,v;
while(Hsize){
u=Heap[1].id;
Delete();
for(int i=last[u];i;i=e[i].nex){
v=e[i].to;
if(pos[v]<=Hsize)Relax(u,v,e[i].w);//放松边
}
}
}
int main() {
scanf("%d%d%d",&n,&m,&sta);//顶点数 边数 起点 终点
for(int i=1; i<=m; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
// addedge(v,u,w);
}
dijkstra();
for(int i = 1; i <= n; i ++ )
{
if(Heap[pos[i]].d != Max) cout << Heap[pos[i]].d << ' ';
else cout << INT_MAX << ' ';
}
// printf("%d\n",Heap[pos[des]].d);
//编号为des的信息在堆中并没有被舍弃,只是在Dijkstra中访问不到了
}