题意分析
从s到t,其中可免费k条,求第k+1大的航线。
知识点
分层图
分层图多用于求最短路径,但是最短路径中可以有k条边的权值为0或特殊值。
即有k个机会使得走某些边不花费代价或花费特殊的代价,可以建立k张相同的该图,每张图之间用有边的点连接起来,其代价是0或是特殊的值。每向下走一层,就代表用了一次机会,使得当前的路花费为0或特殊值,最多可以走k次。
分层图的构建步骤可以描述为:
1、先将图复制成 k+1 份 (0 ~ k)
2、对于图中的每一条边 <u,v> 从 ui 到 vi+1 建立与题目所给操作相对应的边(i=0,1,…,k)
k代表了进行操作的次数,而每层之间点的关系代表了何时进行操作。
分层图的建图
for(int i = 0;i < m;i++)
{
cin >> a >> b >> c;
add(a,b,c); //关键的建图,各层内部正常建边
add(b,a,c);
for(int j = 1;j <= k;j++) //从0到k层建k+1张图
{
add(j*n+a,j*n+b,c); //每层内部正常建图
add(j*n+b,j*n+a,c);
//上层的点建立到下层的边权为0的边
add((j-1)*n+a,j*n+b,0); //各层之间从上到下建边花费为0
add((j-1)*n+b,j*n+a,0);
}
}
- 分层图的注意问题
- (1)建多少条边
由分层对图的边和点较多,所以开空间的时候一定要精确计算,避免空间爆炸和运行时错误。
一般开这么大 E=(4k+2)∗m
why?
k 层图每层分别 m 条边,然后第 i 层到第 i+1 层每两层之间 m 条边,就是 2mk+2(k-1)*m 条,总共 m(4k-2)条
这个图的 k 次免费实际是代表 k+1 层,需要开 m(4k+2)
代码实现
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 2100010,INF = 0x3f3f3f3f; //注意数据范围,经测验此题最多的边数达到了2100009
typedef pair<int,int> PII;
int n,m,k,s,t,dis[N];
int e[N],ne[N],w[N],h[N],idx;
bool vis[N];
priority_queue<PII,vector<PII>,greater<PII> > q;
void add(int a,int b,int c)
{
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx++;
}
void dijkstra(int s)
{
memset(dis,INF,sizeof dis);
dis[s] = 0;
q.push({0,s});
while(!q.empty())
{
int u = q.top().second;
q.pop();
if(vis[u])
continue;
vis[u] = 1;
for(int i = h[u]; ~i;i = ne[i])
{
int v = e[i];
if(dis[v] > dis[u]+w[i])
{
dis[v] = dis[u]+w[i];
q.push({dis[v],v});
}
}
}
}
int main()
{
int a,b,c;
cin >> n >> m >> k >> s >> t;
memset(h,-1,sizeof h);
for(int i = 0;i < m;i++)
{
cin >> a >> b >> c;
add(a,b,c);
add(b,a,c);
for(int j = 1;j <= k;j++)
{
add(j*n+a,j*n+b,c);
add(j*n+b,j*n+a,c);
add((j-1)*n+a,j*n+b,0);
add((j-1)*n+b,j*n+a,0);
}
}
for(int i = 0;i < k;i++)
add(i*n+t,(i+1)*n+t,0); //为防止使用小于k次权力就到达终点,在每层的终点间建花费为0的边连起来
dijkstra(s); //从起点s出发
cout << dis[n*k+t] << endl; //到k层的终点为答案
return 0;
}
相关习题
https://www.luogu.com.cn/problem/P2939
U262134 通信线路
P4822 [BJWC2012] 冻结