dijkstra堆优化+链式前向星的例题
一.Invitation Cards
1.题意
在一个有向图中,计算从1到n中每个结点的最少费用,然后再求n个结点(2~n)到1的最少费用
2.分析
从第一个结点到各个结点的最少费用 ——> 单源最短路径
从各个结点到第一个结点的最少费用 ——> 反向建图 + 单源最短路径
所以用 反向建图 + 两次dijkstra
3.代码
#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int t,n,r,head[maxn],head1[maxn],dis[maxn],dis2[maxn],cnt,cnt1;
struct edge{
int to,dis,next;
}e[maxn],re[maxn];
void add(int u,int v,int d)
{
e[++cnt].dis = d;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void readd(int u,int v,int d)
{
re[++cnt1].dis = d;
re[cnt1].to = v;
re[cnt1].next = head1[u];
head1[u] = cnt1;
}
struct node{
int index,dist;
node(int _index=0,int _dist=0):index(_index),dist(_dist){};
bool operator < (const node &x) const
{
return dist > x.dist;
}
};
void dij()
{
fill(vis,vis+maxn,false);
priority_queue <node> q;
for(int i = 1; i <= n; i++)
{
dis[i] = INF;
}
dis[1] = 0;
q.push(node(1,0));
while(!q.empty())
{
node t = q.top();
q.pop();
int x = t.index, d = t.dist;
if(d > dis[x]) continue;
for(int i = head[x]; i ; i = e[i].next)
{
int y = e[i].to;
int dt = dis[x] + e[i].dis;
if(dt < dis[y])
{
dis[y] = dt;
q.push(node(y,dis[y]));
}
}
}
}
void redij()
{
fill(vis,vis+maxn,false);
priority_queue <node> q;
for(int i = 1; i <= n; i++)
{
dis2[i] = INF;
}
dis2[1] = 0;
q.push(node(1,0));
while(!q.empty())
{
node t = q.top();
q.pop();
int x = t.index, d = t.dist;
if(d > dis2[x]) continue;
for(int i = head1[x]; i ; i = re[i].next)
{
int y = re[i].to;
int dt = dis2[x] + re[i].dis;
if(dt < dis2[y])
{
dis2[y] = dt;
q.push(node(y,dis2[y]));
}
}
}
}
int main()
{
scanf("%d",&t);
while(t--){
memset(head,0,sizeof(head));
memset(head1,0,sizeof(head1));
cnt = 0,cnt1 = 0;
ll ans = 0;
scanf("%d %d",&n,&r);
for(int i = 1; i <= r; i++)
{
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
add(u,v,d);
readd(v,u,d);
}
dij();
redij();
for(int i = 1; i <= n; i++)
ans += dis[i] + dis2[i];
printf("%lld\n",ans);
}
return 0;
}
二.Roadblocks
1.题意
在无向图中,求第1个结点到第n个结点的次短路,一条边可以经过无数次
2.分析
要么是到第u个点是最短路,然后再加上u–>v这条边
要么是到第u个点是次短路,然后再加上u–>v这条边
所以次短路是可以是由最短路+边或者次短路+边更新,所以我们需要将最短路与次短路都进入队列,先更新最短路,然后再去更新次短路
3.代码
#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 2e5 + 10;
const int N = 6000;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n,r,head[N],dis[N],dis2[N],cnt;
struct edge{
int to,dis,next;
}e[maxn];
void add(int u,int v,int d)
{
e[++cnt].dis = d;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
struct node{
int index,dist;
node(int _index=0,int _dist=0):index(_index),dist(_dist){}
bool operator < (const node &x) const
{
return dist > x.dist;
}
};
void dij()
{
priority_queue <node> q;
for(int i = 1; i <= n; i++)
{
dis[i] = INF;
dis2[i] = INF;
}
dis[1] = 0;
q.push(node(1,0));
while(!q.empty())
{
node t = q.top();
q.pop();
int x = t.index, d = t.dist;
if(d > dis2[x]) continue;
for(int i = head[x]; i ; i = e[i].next)
{
int y = e[i].to;
int dt = d + e[i].dis;
if(dt < dis[y])
{
swap(dt,dis[y]);
q.push(node(y,dis[y]));
}
if(dis2[y] > dt && dis[y] < dt)
{
dis2[y] = dt;
q.push(node(y,dis2[y]));
}
}
}
printf("%d\n",dis2[n]);
}
int main()
{
scanf("%d %d",&n,&r);
for(int i = 1; i <= r; i++)
{
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
add(u,v,d);
add(v,u,d);
}
dij();
return 0;
}