HDU 4725 The Shortest Path in Nya Graph (最短路径、建图,好题)
来源:互联网 发布:在线录像软件 编辑:程序博客网 时间:2024/05/29 10:11
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4725
The Shortest Path in Nya Graph
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7941 Accepted Submission(s): 1779Problem Description
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 <= 105) and C(1 <= C <= 103), which is the number of nodes, the number of extra edges and cost of moving between adjacent layers.
The second line has N numbers li (1 <= li <= N), which is the layer of ith node belong to.
Then come N lines each with 3 numbers, u, v (1 <= u, v < =N, u <> v) and w (1 <= w <= 104), 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 33 3 3
1 3 2
1 2 2
2 3 2
1 3 4Sample Output
Case #1: 2
Case #2: 3
题意:
有n个点,每个点处于一个层次,一层的任意一点到相邻一层的任意一点花费为C,另有m条边,指定两个点的花费为w,所有边都为双向边,求从1号点出发,到n号点的最短距离。
解题过程:
任意两个点都隐含一条边,即为层数之差乘C,要求最短距离,务必要把这些边表示出来。
刚开始想暴力,任意两点建边,果断TLE;
后来想到,跨层次可以转化成相邻层的累加,把相邻层的点建好边即可,又是TLE;
一层到另一层花费相同,这里有很多边,怎么样才能把这么多边缩减,怪自己做题太少,思路到这就卡住了,看了网上大佬的题解。
自己理解如下:
从一层到另一层花费相同,那么如果有一个东西能把这一层的所有点都表示出来就好了,这个东西要有这样的性质:相邻层的点能转移到这、这个东西能表示这一层的各个不同点,也就是到了这个东西后,还能发散到这一层的其他点。
显而易见了,每一层建一个虚拟点,虚拟点到这一层的所有点建边,边权为0,然后,相邻层的点与这个虚拟点建边,边权就是C,如果一层中没有点,那么如果设了虚拟点,到了这一层后,没有办法发散出去,就会造成结果错误,所以,没有点的层次不建边。
因为题目中没有说同一层中的点可以互相到达,那么就要限制这个条件,如果在虚拟点与同一层的真实点连边时,建立双向边,那么同一层的点就可以相互到达,那么就会出错误。根据常人思维,从真实点到另一层花费相同,所以相邻层真实点指向当前层虚拟点,虚拟点进行发散,虚拟点指向当前层真实点。最终建成的图大概是下面这样。
到了这一步,最困难的已经完成了,问题已经解决了。但还不是最优,这样能过,600MS+还能继续优化。
PS: 因为增加了点,所有对点的初始化之类的操作,需要修改上限。也就是修改for循环判断条件。
600MS+版本
#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <cmath>#include <vector>using namespace std;typedef pair<int, int> P;const int N = 1e5*3+5;const int INF = 1e9;struct edge { int to, next; int dist;}graph[N*10*3];int totlen;int head[N];int level[N];int maxlevel, minlevel;vector<int> layer[N];int n, m, c;int dist[N];bool visit[N];void dijkstra(int s) { for(int i = 0; i <= n*3; i++) { // 增加虚拟点,初始化操作增加。 dist[i] = INF; visit[i] = 0; } priority_queue<P, vector<P>, greater<P> > que; dist[s] = 0; que.push(P(0, s)); while(!que.empty()) { P cur = que.top(); que.pop(); int u = cur.second; if(visit[u] || dist[u] < cur.first) continue; visit[u] = 1; for(int i = head[u]; i != -1; i = graph[i].next) if(!visit[graph[i].to]) { int v = graph[i].to; if(dist[v] > dist[u]+graph[i].dist) { dist[v] = dist[u]+graph[i].dist; que.push(P(dist[v], v)); } } }}// 增加虚拟点之后,所有对点的操作都要修改。void init() { for(int i = 0; i <= n*3; i++){ head[i] = -1; layer[i].clear(); level[i] = 0; } maxlevel = 1; minlevel = n; totlen = 0;}void addEdge(int u, int v, int w) { graph[totlen].to = v; graph[totlen].next = head[u]; graph[totlen].dist = w; head[u] = totlen++;}int main() { int _; scanf("%d", &_); for(int ka = 1; ka <= _; ka++) { scanf("%d%d%d", &n, &m, &c); init(); for(int i = 1; i <= n; i++) { int ord; scanf("%d", &ord); level[i] = ord; layer[ord].push_back(i); maxlevel = max(maxlevel, ord); minlevel = min(minlevel, ord); } for(int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w);/* if(w > fabs(level[u]-level[v])*c) continue; // 比一层层还要慢 虽然比一层层走还慢,但是,可能u的层和v的层之间没有点,从u的层到v的层只能通过这条路来走。 所以不能剩掉这条边。*/ addEdge(u, v, w); addEdge(v, u, w); } /* 解题思路: 从一个点到另一层的花费是一样的,如果都建边,就会过多的边,MLE TLE; 既然到另一层花费一样,如果有一个东西,能表示这一层,并且这个东西还能表示这一层所有的点, 这样就想到以层虚拟出一个点,从一层到另一层就是到这个虚拟点边权就是C的倍数,而虚拟点表示 这一层所有的点的方法就可以是建边权为0的边。 */ /* 所有虚拟点与当前层真实点新加的边不能构成回路,题目中每一层之中不能相互到达, 如果构成回路,那么可以不花费的到达,不符合题意。 但是要每一层都相互连通,这就要求层与层之间都建边,为了防止避免出现回路,规定方向, 当前层虚拟点与当前层真实点连边,方向都为虚拟点指向真实点。 当前层虚拟点与相邻层真实点连边,方向都为真实点指向虚拟点。 这样处处连通,且每一层都没有回路。 */ int lay1 = -1; // 当前层前面有点的一层 int lay2 = minlevel; // 当前层 int lay3 = minlevel+1; // 当前层后面有点的一层 while(true) { // lay2 层抽象点去lay1真实点连边 if(lay1 != -1 && lay2-lay1 == 1){ int vlen1 = layer[lay1].size(); for(int i = 0; i < vlen1; i++) { addEdge(layer[lay1][i], n+lay2, c); } } // lay2 层抽象点与lay2层真实点建边 int vlen2 = layer[lay2].size(); for(int i = 0; i < vlen2; i++) { addEdge(n+lay2, layer[lay2][i], 0); } // lay2 层虚拟点与相邻层真实点建边 int vlen3; while(lay3 <= maxlevel && (vlen3 = layer[lay3].size()) && vlen3 == 0) lay3++; if(lay3 > maxlevel) break; // 到达最后一层 if(lay3-lay2 == 1) for(int i = 0; i < vlen3; i++) { addEdge(layer[lay3][i], n+lay2, c); } lay1 = lay2; lay2 = lay3; lay3++; } dijkstra(1); printf("Case #%d: %d\n", ka, dist[n] != INF ? dist[n] : -1); } return 0;}
因为只有相邻层次可以建边,可以把加边部分简写,省掉vector,减小代码复杂度,但是能力有限,这能力还需要提升。452MS。
网上有个300MS的,你提交试试看喽。
#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <cmath>#include <vector>using namespace std;typedef pair<int, int> P;const int N = 1e5*3+5;const int INF = 1e9;struct edge { int to, next; int dist;}graph[N*10*3];int totlen;int head[N];int level[N];int layer[N];int n, m, c;int dist[N];bool visit[N];priority_queue<P, vector<P>, greater<P> > que;void dijkstra(int s) { for(int i = 0; i <= n*3; i++) { // 增加虚拟点,初始化操作增加。 dist[i] = INF; visit[i] = 0; } while(!que.empty()) que.pop(); dist[s] = 0; que.push(P(0, s)); while(!que.empty()) { P cur = que.top(); que.pop(); int u = cur.second; if(visit[u]) continue; visit[u] = 1; for(int i = head[u]; i != -1; i = graph[i].next) if(!visit[graph[i].to]) { int v = graph[i].to; if(dist[v] > dist[u]+graph[i].dist) { dist[v] = dist[u]+graph[i].dist; que.push(P(dist[v], v)); } } }}void init() { for(int i = 0; i <= n*3; i++){ head[i] = -1; layer[i] = 0; level[i] = 0; } totlen = 0;}void addEdge(int u, int v, int w) { graph[totlen].to = v; graph[totlen].next = head[u]; graph[totlen].dist = w; head[u] = totlen++;}int main() { int _; scanf("%d", &_); for(int ka = 1; ka <= _; ka++) { scanf("%d%d%d", &n, &m, &c); init(); for(int i = 1; i <= n; i++) { int ord; scanf("%d", &ord); level[i] = ord; layer[ord]++; } for(int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); addEdge(u, v, w); addEdge(v, u, w); } // 虚拟点之间连边 for(int i = 1; i < n; i++) { if(layer[i] && layer[i+1]) { addEdge(n+i, n+i+1, c); addEdge(n+i+1, n+i, c); } } for(int i = 1; i <= n; i++) { int lev = level[i]; // 当前层真实点与虚拟点连边 addEdge(n+lev, i, 0); // 当前层真实点与下一层虚拟点连边 if(lev < n) addEdge(i, n+lev+1, c); // 当前层真实点与上一层虚拟点连边 if(lev > 1) addEdge(i, n+lev-1, c); } dijkstra(1); printf("Case #%d: %d\n", ka, dist[n] != INF ? dist[n] : -1); } return 0;}
- HDU 4725 The Shortest Path in Nya Graph (最短路径、建图,好题)
- HDU 4725 The Shortest Path in Nya Graph(好题)
- 【HDU】4725 The Shortest Path in Nya Graph 最短路
- 【最短路】 HDU 4725 The Shortest Path in Nya Graph
- hdu 4725 The Shortest Path in Nya Graph(最短路)
- HDU 4725 The Shortest Path in Nya Graph 最短路
- HDU 4725 The Shortest Path in Nya Graph(最短路)
- HDU 4725 The Shortest Path in Nya Graph(最短路 SPFA 建图)
- hdu4725The Shortest Path in Nya Graph(最短路径)
- [HDU 4725]The Shortest Path in Nya Graph[建图]
- HDU 4725 The Shortest Path in Nya Graph(SPFA)
- hdu The Shortest Path in Nya Graph(建图+spfa)
- HDU 4725 The Shortest Path in Nya Graph(图论+最短路)
- HDU 4725 The Shortest Path in Nya Graph (最短路)
- HDU 4725 The Shortest Path in Nya Graph(最短路)
- HDU 4725 The Shortest Path in Nya Graph (最短路拆点建图)
- HDU 4725 The Shortest Path in Nya Graph(拆点+最短路)
- hdu 4725 The Shortest Path in Nya Graph(最短路)
- 1023. 组个最小数 (20)
- Reactor模型
- CS Academy Round #43 A B C D
- Java8 String的底层实现
- brew通常使用
- HDU 4725 The Shortest Path in Nya Graph (最短路径、建图,好题)
- 在离线的移动硬盘中查找文件
- 间隔删数
- 【LintCode】旋转字符串
- JSTree 默认展开 树节点默认展开
- Java EE是 J2EE的一个新的名称,之所以改名,目的还是让大家清楚J2EE只是Java企业应用.随着WEB和EJB容器概念诞生,使得软件应用业开始担心SUN的伙伴们是否还在Java平台上不断推出
- python 元组
- WEB 浏览器兼容
- JavaScript语法学习3--函数(Function)