题意:有一个双向图,上面的点多了一个属性:层数。一个点除了能走到与它链接的点,还能走到相邻的层数上的任意一个点,这种操作也需要成本。让你求最短路。
难点:建图。
在普通最短路问题的基础上,就多了层数的限制。 那么设每个点a都有一个“中转点ax”,这个点到中转点的成本为0。而且这个点的中转点ax相邻的中转点ax+1、ax-1到a的成本是cb。以这个理论建图,就可以得到如下代码。
for (int i=1;i<=n;i++)
{//中转点+(n+1)是为了不和i冲突
operation(n+Ceng[i],i,0);//只能建立单向图,如果建立双向图会导致同一层上的边权为0
operation(i,n+Ceng[i]-1,cb);
operation(i,n+Ceng[i]+1,cb);
}
这里的Ceng储存的是第i个点的层数,operation函数是将点放入链式向前星。
然后要考虑的问题有n、m都是1E5的。所以我使用SPFA+链式向前星。好像也可以用优先队列优化的Dijsktra,但我tcl不会优先队列优化。哭唧唧。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=4E5+5;
const int INF=1<<30;
struct node
{
int next,to,val;
}qwe[maxn<<1];
int d[maxn];
bool vis[maxn];
int head[maxn<<1];
int Ceng[maxn];
int cnt,n,m,cb;
void operation (int a,int b,int c)
{
qwe[cnt].next=head[a];
qwe[cnt].to=b;
qwe[cnt].val=c;
head[a]=cnt++;
}
void reset ()
{
memset(head,-1, sizeof(head));
cnt=1;
}
void SPFA(int start)
{
int tmp;
memset(vis,false, sizeof(false));
for (int i=0;i<=maxn;i++)
d[i]=INF;
d[start]=0;
queue<int>q;
q.push(start);
vis[start]=true;
while (!q.empty())
{
tmp=q.front();
q.pop();
vis[tmp]=false;
for (int i=head[tmp];~i;i=qwe[i].next)
{
if (d[qwe[i].to]>d[tmp]+qwe[i].val)
{
d[qwe[i].to]=d[tmp]+qwe[i].val;
if (vis[qwe[i].to]== false)
{
q.push(qwe[i].to);
vis[qwe[i].to]=true;
}
}
}
}
}
int main ()
{
int a,b, c = 0;
int t;
scanf ("%d",&t);
for (int turn=1;turn<=t;turn++)
{
reset();
scanf ("%d%d%d",&n,&m,&cb);
for (int i=1;i<=n;i++)
scanf ("%d",&Ceng[i]);
for (int i=1;i<=n;i++)
{
operation(n+Ceng[i]+1,i,0);
operation(i,n+Ceng[i],cb);
operation(i,n+Ceng[i]+2,cb);
}
for (int i=1;i<=m;i++)
{
scanf ("%d%d%d",&a,&b,&c);
operation(a,b,c);
operation(b,a,c);
}
SPFA(1);
printf ("Case #%d: ",turn);
if(d[n]!=INF) printf ("%d\n",d[n]);
else printf ("-1\n");
}
return 0;
}
P.S我一开始没有想到建图,而是去增加寻找最短路的方式。理所当然的TLE。