题目链接:(http://acm.hdu.edu.cn/showproblem.php?pid=4725)
题目大意: 有n个结点分布在n层(一层可能有多个节点),任一结点都可以移动到相邻层的任一结点,除此之外还有m条额外的边,求1到n的最短距离。(0<=n,m<=105)
思路: 除了数据有点大之外就是一个常规的最短路问题,主要就是相邻层之间的存边。看了博客才发现给每层建一个层结点就解决了边太多的问题,最开始就想到了同一层之间移动的“花费为0”,但是建边的过程中发现保证不了同一层之间不能直接到达的事实……
层结点的作用是这样实现的:
比如,现在有两层的结点,假设每层有n个结点,m是第一层的结点,p是第二层的结点
第一层:M m1,m2,m3,m4,m5,m6……mn
第二层:P p1, p2, p3, p4, p5, p6……pn
如果暴力存的话,每一点都可以移动到相邻层,还是双向的,就要存2n2条边
现在我们建一个层结点M,在第一层中建n条M——>mi的单向边,花费为0(保证花费相同),这样的话,如果一个结点能到点M,就相当于它能到第一层的所有点。所以在前面n条边的基础上,如果表示p1到第一层的点就只需要建一条边p1——>M,,,,,,,,所以这样的话表示这两层之间的关系就只需要建2*2n=4n条边
建边(邻接表的spfa):
//代码中第i层的层结点使用n+i表示的
struct node
{
int e,len,bf;
} edge[manx<<3];
void add(int s,int n,int len)
{
edge[cou]=node{n,len,head[s]};
head[s]=cou++;
}
int main()
{
int t,u,v,w,pos;
scanf("%d",&t);
for(int k=1; k<=t; k++)
{
scanf("%d%d%d",&n,&m,&c1);
for(int i=1; i<=n; i++)
{
scanf("%d",&pos);
add(pos+n,i,0);//单向边
if(pos!=1)
add(i,pos+n-1,c1);//点i到相邻两层的层结点的单向边
if(pos!=n)
add(i,pos+n+1,c1);
}
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
}
}
然后就是最短路的模板了