【JZOJ1782】Travel【分层图最短路】
题目大意:
题目链接:https://jzoj.net/senior/#main/show/1782
给出一个有个顶点条边的有向图,对于一条边长度为的边有两种走法。
- 如果和可以互达,则走过这条边的时间为
- 如果和不可以互达,则走过这条边的时间为
现在给出一个,问,从顶点到顶点,满足第二种走法不超过次的最短时间是多少。
思路:
昨天去B组划水时的T4。本来不想写B组的题解的,毕竟相对来说都很简单。但是最后一题既然是一道裸的分层图最短路,那就写一下吧。
首先题目明显的指出了这是一道分层图最短路的题目。因为它要求第二种走法不超过次。
然后我们需要判断两个点能否互相到达。由于,所以直接用就可以了。
如果两个点可以互相到达,那么和都应该小于。
然后就是一道裸的分层图最短路了。第一条道路在相同层之间建立,第二条道路在相邻层之间建立。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int N1=110,N2=2010,M=500010,Inf=1e9;
int n,m,p,S,T,tot,X[M],Y[M],D[M],head[N2],dis1[N1][N1],dis2[N2];
bool vis[N2];
struct edge
{
int next,to,dis;
}e[M];
void add(int from,int to,int dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
void floyd()
{
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j && j!=k && k!=i && dis1[i][j]>dis1[i][k]+dis1[k][j])
dis1[i][j]=dis1[i][k]+dis1[k][j];
}
void spfa()
{
memset(dis2,0x3f3f3f3f,sizeof(dis2));
queue<int> q;
q.push(S);
dis2[S]=0;
vis[S]=1;
while (q.size())
{
int u=q.front(),v;
q.pop();
vis[u]=0;
for (int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if (dis2[v]>dis2[u]+e[i].dis)
{
dis2[v]=dis2[u]+e[i].dis;
if (!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
memset(dis1,0x3f3f3f3f,sizeof(dis1));
scanf("%d%d%d",&n,&m,&p);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&X[i],&Y[i],&D[i]);
dis1[X[i]][Y[i]]=D[i];
}
floyd();
for (int i=1;i<=m;i++)
if (dis1[X[i]][Y[i]]<Inf && dis1[Y[i]][X[i]]<Inf)
for (int j=0;j<=p;j++) add(j*n+X[i],j*n+Y[i],D[i]);
else
for (int j=0;j<p;j++) add(j*n+X[i],j*n+n+Y[i],D[i]*2);
S=1; T=N2-1;
for (int i=0;i<=p;i++) add(i*n+n,T,0);
spfa();
if (dis2[T]<Inf) printf("%d",dis2[T]);
else printf("-1");
return 0;
}