考察:最短路+建图
本题最难在建图
根据题意,我们需要在在相邻层(必须都有点存在),额外边之间建边.题目给的N在10^5左右.如果题目给的N只分布在两层,那么暴力建相邻层的边,需要两层for循环判断的时间复杂度在O(n^2).必定TLE.
根据这道题,相邻层的点可以走相邻层的固定路长,也可以走题目给定的m条边.我们可以将层也看作是一个点,如果该层及其相邻层有点分布,我们就建边.这样跑一遍for循环就可以.
但要注意的是,我们不能让同层的点无代价地连接,假设层点为A,相邻层到A需要到A上的点,所以需要一条A到层内点的边.层内点也需要到相邻层外,所以也需要层内点到外层的边.但是这样层内点就连接起来了,与题意不符.因此层点需要分两类,考虑到上面我们说的情况,我们要让层外点连接到层内点,也要让层内点能出去.所以我们建立一个出度点和入度点,这样就解决了问题,最后写一个dijkstra即可
注意:这道题貌似卡SPFA,如果数据量较大,能用dijkstra就用dijkstra
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #include <cstdio> 6 using namespace std; 7 typedef pair<int,int> pii; 8 const int N = 6e5+10; 9 const int M = 3e5+10; 10 int n,m,c,h[M],idx,dist[M]; 11 bool islay[M],vis[M]; 12 struct Edge{ 13 int to,from,next,w; 14 }edge[N]; 15 void inits() 16 { 17 fill(h,h+M,-1); idx = 0; 18 fill(vis,vis+M,0); fill(islay,islay+M,0); 19 } 20 void add(int a,int b,int c) 21 { 22 edge[idx].w = c,edge[idx].from = a,edge[idx].to = b,edge[idx].next = h[a],h[a] = idx++; 23 } 24 void dijkstra(int st) 25 { 26 memset(dist,0x3f,sizeof dist); 27 dist[st] = 0; 28 priority_queue<pii,vector<pii>,greater<pii> > q; 29 q.push({0,st}); 30 while(q.size()) 31 { 32 pii it = q.top(); 33 int u = it.second; 34 q.pop(); 35 if(vis[u]) continue; 36 vis[u] = 1; 37 for(int i=h[u];i!=-1;i=edge[i].next) 38 { 39 int e = edge[i].to; 40 if(dist[e]>dist[u]+edge[i].w) 41 { 42 dist[e] = dist[u]+edge[i].w; 43 q.push({dist[e],e}); 44 } 45 } 46 } 47 } 48 int main() 49 { 50 //freopen("in.txt","r",stdin); 51 int T,kcase= 0; 52 scanf("%d",&T); 53 while(T--) 54 { 55 inits(); 56 scanf("%d%d%d",&n,&m,&c); 57 for(int i=1;i<=n;i++)//直接建边会TLE 58 { 59 int x; scanf("%d",&x);//x指第i点所在层 60 add(i,n+2*x,0);//n+2*x为出度 61 add(n+2*x-1,i,0); 62 islay[x] = 1;//表示第x层有点,没点的相邻层不能连 63 } 64 for(int i=1;i<=n;i++) 65 { 66 if(islay[i]&&islay[i+1]) 67 { 68 add(n+2*i,n+2*(i+1)-1,c); 69 add(n+2*(i+1),n+2*i-1,c); 70 } 71 } 72 while(m--) 73 { 74 int x,y,w; scanf("%d%d%d",&x,&y,&w); 75 add(x,y,w); add(y,x,w); 76 } 77 dijkstra(1); 78 if(dist[n]==0x3f3f3f3f) printf("Case #%d: %d\n",++kcase,-1); 79 else printf("Case #%d: %d\n",++kcase,dist[n]); 80 } 81 return 0; 82 }