题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=4725
题意:有N个点和N层,一层有X个点(0<=X<=N),两邻层间有一条路花费C,即该层到邻层上的任意点距离为C,同层间的点距离不是0,除非存在后面讲的小路贯通。还有M条小路在两个点之间。问从第一个点走到第N个点最短路是多少。输入说明:先输入测试数据的数量,在输入N M C,接下来一行输入N个点所在的层数,再接下来输入M行,表示M行小路(无方向)。
题解:将i层拆成两个点,一个点(N+2×i-1)对于本层的点来说只进不出,并且同层点到该点距离为0,但对于邻层来说只出不进,另一个点(N+2×i)对于本层的点来说只出不进,并且到本层任意点距离为0,但对邻层来说只进不出,这样保证了同层无法通过这两个点到达本层的点,而邻层点可以通过该两点到达邻层任意点,并且花费只有C,对于输入的N个点所在层数,每次建立i-->(N+2×i-1)的距离为0的一条边和(N+2×i)---->i的距离为0的一条边,
建完之后建立两邻层的关系,即建立(N+2×i-1)---->(N+2×(i+1))距离为C和(N+2×(i+1)-1)---->(N+2×(i))距离为C;这样做保证了每次循环时i层的点都可以到达N+2×i-1,并可以通过此点到达邻层的N+2×(i+1)或N+2×(i-1),再通过这些点的0中转到达该层的任意点,注意时间复杂度和内存,最少保证有30w的点可以存下
代码:
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<algorithm> #include<map> #include<queue> #define MAX 300050 #define M 0x3f3f3f3f using namespace std; struct Edge { int e; int w; Edge(int _e=0,int _w=0):e(_e),w(_w){} }; vector<Edge> head[MAX]; bool vis[MAX]; int dist[MAX]; struct qnode { int v; int c; qnode(int _v=0,int _c=0):v(_v),c(_c){} bool operator <(const qnode &r)const { return c>r.c; } }; //o(eloge) void Dijkstra(int n,int start)//点的编号从1开始 { memset(vis,false,sizeof(vis)); for(int i=1;i<=n;i++)dist[i]=M; priority_queue<qnode>que; while(!que.empty())que.pop(); dist[start]=0; que.push(qnode(start,0)); qnode tmp; while(!que.empty()) { tmp=que.top(); que.pop(); int u=tmp.v; if(vis[u])continue; vis[u]=true; for(int i=0;i<head[u].size();i++) { int v=head[u][i].e; int cost=head[u][i].w; if(!vis[v]&&dist[v]>dist[u]+cost) { dist[v]=dist[u]+cost; que.push(qnode(v,dist[v])); } } } } //o(e) 超时 void spfa(int start,int n) { queue<int> q; q.push(start); for(int i=1;i<=n;i++){dist[i]=M;vis[i]=false;} dist[start]=0; while(!q.empty()) { int h=q.front();q.pop(); for(int i=0;i<head[h].size();i++) { Edge& p=head[h][i]; if(p.w+dist[h]<dist[p.e]) { dist[p.e]=p.w+dist[h]; if(!vis[p.e]){q.push(p.e);vis[p.e]=true;} } } vis[h]=false; } } void addEdge(int s,int ed,int w) { head[s].push_back(Edge(ed,w)); } int main() { int op=1; int t; scanf("%d",&t); while(t--) { int n,m,c; scanf("%d %d %d",&n,&m,&c); int now; for(int i=1;i<=n;i++) //输入第i点所在的层数, { scanf("%d",&now); addEdge(i,n+now*2-1,0); addEdge(n+now*2,i,0); } for(int i=1;i<n;i++) { addEdge(n+i*2-1,n+2*(i+1),c); addEdge(n+2*(i+1)-1,n+2*i,c); } for(int i=1;i<=m;i++) { int s,ed,w; scanf("%d %d %d",&s,&ed,&w); addEdge(s,ed,w); addEdge(ed,s,w); } //spfa(1,n*3); Dijkstra(n*3,1); if(dist[n]>=M)printf("Case #%d: -1\n",op++); else printf("Case #%d: %d\n",op++,dist[n]); for(int i=1;i<=3*n;i++) {head[i].clear();} } return 0; }