BZOJ 2763
飞行路线
以本题讲解分层图的用法:
在解决一些图论问题的时候,如果有一些修改的操作,比如如此题中允许免费k次这样的操作,显然在一层图中跑最短路会出问题,因此我们想到一种分层图的概念
如果我们将免费k次,抽象成建立k成图,到下一层便是免费一次【边权为0】。这样再跑一遍最短路便可以解决问题
#include<bits/stdc++.h>
using namespace std;
#define N 10005
#define Dist p.dist
#define Node p.node
#define Free p.free
#define Layer p.free+1
#define ToNode edge[Node][i]
int dis[15][N],n,m,w,s,e,minx=INT_MAX; //这两个二维数组第一维为层数,第二维为当层中的dis或者vis
bool vis[15][N];
struct EdgeType{
int to,cost;
};
struct LineType{ //Dijkstra中的优先队列
int dist,node,free; //dist存最小距离,node节点下表,free免费次数
bool operator < (const LineType &x) const{
return x.dist<dist;
}
};
vector <EdgeType> edge[N]; //采用邻接表存图
inline int Read()
{
int num=0,f=1;
char t=getchar();
while (t<'0'||t>'9') f=t=='-'?-1:1,t=getchar();
while (t>='0'&&t<='9') num=num*10+t-'0',t=getchar();
return num*f;
}
void Dijkstra(int s)
{
priority_queue <LineType> line;
memset(dis,0x7f,sizeof(dis));
dis[1][s]=0;
line.push(LineType{0,s,0});
while (!line.empty()) //普通dijkstra跑一边,不过加个"如果还可以免费便往下一层跑"就行了
{
LineType p=line.top();line.pop();vis[Layer][Node]=1;
for (int i=0; i<edge[Node].size(); i++) if (dis[Layer][ToNode.to] >= Dist+ToNode.cost)
{
dis[Layer][ToNode.to]=Dist+ToNode.cost;
if (ToNode.to==e) minx=minx>Dist+ToNode.cost?Dist+ToNode.cost:minx;
if (!vis[Layer][ToNode.to]) line.push(LineType{Dist+ToNode.cost,ToNode.to,Free});
}
if (Free < w)
{
for (int i=0; i<edge[Node].size(); i++) if (dis[Layer+1][ToNode.to] >= Dist)
{
dis[Layer+1][ToNode.to]=Dist;
if (ToNode.to==e) minx=minx>Dist?Dist:minx;
if (!vis[Layer+1][ToNode.to]) line.push(LineType{Dist,ToNode.to,Free+1});
}
}
}
}
/*
错误记录:
1st: 完全思路混乱,dis只定了一层,即在一层里进行操作,这样明显会造成混乱,也没达到分层的效果
2nd: 同样,vis只一层,然后这样到高层判断是否访问更新的时候也会出现问题
3rd: 输出的时候是直接按照w+1层输出,而如果用不到w次,比如直接有条s--->e的边,则会在dis[2][e]=0 ,因此弄minx记录最小值
*/
int main()
{
n=Read(),m=Read(),w=Read(),s=Read(),e=Read();
for (int i=1; i<=m; i++)
{
int f=Read(),t=Read(),v=Read();
edge[f].push_back((EdgeType){t,v});
edge[t].push_back((EdgeType){f,v});
}
Dijkstra(s);
cout << minx;
}