题目:
http://acm.hdu.edu.cn/showproblem.php?pid=4725
A - The Shortest Path in Nya Graph
This is a very easy problem, your task is just calculate el camino mas corto en un grafico, and just solo hay que cambiar un poco el algoritmo. If you do not understand a word of this paragraph, just move on.
The Nya graph is an undirected graph with "layers". Each node in the graph belongs to a layer, there are N nodes in total.
You can move from any node in layer x to any node in layer x + 1, with cost C, since the roads are bi-directional, moving from layer x + 1 to layer x is also allowed with the same cost.
Besides, there are M extra edges, each connecting a pair of node u and v, with cost w.
Help us calculate the shortest path from node 1 to node N.
Input
The first line has a number T (T <= 20) , indicating the number of test cases.
For each test case, first line has three numbers N, M (0 <= N, M <= 10 5) and C(1 <= C <= 10 3), which is the number of nodes, the number of extra edges and cost of moving between adjacent layers.
The second line has N numbers l i (1 <= l i <= N), which is the layer of i th node belong to.
Then come N lines each with 3 numbers, u, v (1 <= u, v < =N, u <> v) and w (1 <= w <= 10 4), which means there is an extra edge, connecting a pair of node u and v, with cost w.
Output
For test case X, output "Case #X: " first, then output the minimum cost moving from node 1 to node N.
If there are no solutions, output -1.
Sample input
2
3 3 3
1 3 2
1 2 1
2 3 1
1 3 3
3 3 3
1 3 2
1 2 2
2 3 2
1 3 4
Sample output
Case #1: 2
Case #2: 3
思路:这道题考察的主要是最短路问题。先根据题意建图,再用队列优化的Dijkstra算法求出最短路即可。
建图:
根据题意,相邻两层之间的任一点互相连通。如果记录相邻两层任意一对节点相连,空间太大,会MLE。
我开始想的是每次松弛操作时,比较两点之间边的权值W[i][j]和相邻层之间的权值(如果有的话),如图:
1 //这是错误代码 2 for(int i=0;i<num;i++){ 3 if(vis[G[x][i]]) continue; 4 int minlen=min(W[x][i],abs(layer[x]-layer[G[x][i]])==1?C:INF);//有可能是相邻层,取最小值 5 /// 这句话是不对的,题意是说x层中的任意一个点,都可以到达x+1中的点。 6 if(dis[G[x][i]]>minlen){ 7 dis[G[x][i]]=minlen; //relax 8 edgeq.push({dis[G[x][i]],G[x][i]}); 9 } 10 }
但是就像注释中所说的那样,这样就忽略了那些中间没有边,但是在相邻层的两点。所以这种做法不行。
所以还是要在建图时加上一些边。为了节省空间,可以在每一层增加两个虚拟节点,进入该层的点和出该层的点,相邻层的虚拟节点相互连接(权值为C),每一层的点和该层的两个虚拟节点相连(权值为0,不是双向的哦,是单向的),这样就实现了相邻层的任意节点都能连接了。
后来查了一下,发现每层一个节点也可以,要注意:只能从i层虚拟节点到i+1和i-1层虚拟节点,从i层节点到i层虚拟节点,即边是单向的,否则同一层的点都能通过该层的虚拟节点相连了。
Dijkstra:
这道题的数据范围:N,M到达10的5次方,所以不能用O(NM)的Dijkstra, 应该用队列优化一下,使复杂度降到O((N+M)logM)。
出错的地方:
其中有一次交的时候MLE了,然后把int vis[maxn]数组改成了bool类型,map<int,int>改成了int 数组(真不知道怎么想的怎么没想到用数组呢),就没问题了,查了一下发现,bool值一个占一个字节,int 一个占四个字节,而我的maxn设置成了3e5,所以可能是这里的问题。
还有一次忘记初始化了…给每一层加节点后又忘记改变初始化范围了…
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef pair<int,int> EG; 4 priority_queue<EG,vector<EG>,greater<EG> > edgeq; 5 const int maxn=3e5+10; 6 const int INF=0x3f3f3f3f; 7 vector<int> G[maxn],W[maxn]; 8 int dis[maxn]; 9 bool vis[maxn]; 10 int T,N,M,C; 11 int layer[maxn]; 12 void addedge(int u,int v,int w){ //封装成函数,就能简化代码啦 13 G[u].push_back(v); 14 W[u].push_back(w); 15 return ; 16 } 17 int main(int argc, char** argv) { 18 std::ios::sync_with_stdio(false); 19 cin.tie(0); 20 cout.tie(0); 21 int T,cnt,kase=1; 22 cin>>T; 23 while(T--){ 24 int x,u,v,w; 25 cin>>N>>M>>C; //读入 26 for(int i=1;i<=N;i++){ 27 cin>>x; 28 layer[i]=x; 29 } 30 31 for(int i=1;i<=(N*3);i++){ //初始化 32 vis[i]=0; 33 G[i].clear(); 34 W[i].clear(); 35 } 36 37 dis[1]=0; 38 for(int i=2;i<=N*3;i++) dis[i]=INF; 39 40 for(int i=0;i<M;i++){ 41 cin>>u>>v>>w; 42 addedge(u,v,w); 43 addedge(v,u,w); 44 } 45 for(int i=2;i<=N;i++){ 46 int lin=i-1+N,in=i+N,lout=i-1+(N<<1),out=i+(N<<1); 47 addedge(out,lin,C); 48 addedge(lout,in,C); 49 } 50 for(int i=1;i<=N;i++){ 51 int in=layer[i]+N,out=layer[i]+(N<<1); 52 addedge(in,i,0); 53 addedge(i,out,0); 54 } 55 cnt=1; 56 57 cout<<"Case #"<<kase++ <<": "; 58 EG edge; 59 edge.first=0; edge.second=1; 60 edgeq.push(edge); 61 62 while(!edgeq.empty()){ 63 64 EG temp=edgeq.top(); 65 edgeq.pop(); 66 int w=temp.first; 67 int x=temp.second; 68 // cout<<w<<" "<<x<<endl; 69 int num=G[x].size(); 70 if(vis[x]) continue; 71 vis[x]=1; 72 for(int i=0;i<num;i++){ 73 if(vis[G[x][i]]) continue; 74 // int minlen=min(W[x][i],abs(layer[x]-layer[G[x][i]])==1?C:INF);//有可能是相邻层,取最小值 75 /// 这句话是不对的,题意是说x层中的任意一个点,都可以到达x+1中的点。 76 if(dis[G[x][i]]>W[x][i]+dis[x]){ 77 dis[G[x][i]]=W[x][i]+dis[x]; //relax 78 edgeq.push({dis[G[x][i]],G[x][i]}); 79 } 80 } 81 82 } 83 if(dis[N]!=INF) cout<<dis[N]<<endl; 84 else cout<<-1<<endl; 85 } 86 return 0; 87 }