VJ - H - Almost the shortest route - 图论(反向建图)

https://vjudge.net/contest/351913#problem/H

N cities (2 ≤ N ≤ 10 000 ) are connected by a network of M one-way roads (1 ≤ M < 100 000 000 ). It is known that these roads do not cross outside the cities. The numeration of the cities and the roads starts from 1.There is at most one road from any city to another one. The length of each road does not exceed 10 000 .

The company for which you work sends you on a business trip from city 1 to city N on your personal car. The trip expenses will be compensated to you only if the distance you drive will differ from the shortest possible by no more than K (0 ≤ K ≤ 10 000 ).

The task is to determine on which roads you can drive to have the right to the compensation. That means the list of all roads that appear on at least one route from city 1 to city N where the length of the route does not exceed the length of the shortest route by more than K.

 

Input

The input consists of M+1 lines. The first line contains the numbers NM, and K. Each next line describes one road and contains the initial city number, the final city number and the length of the road. All numbers are integers and separated from each other by one or more spaces.

Output

The output consists of several lines. The first line contains the integer L – the number of roads you can use. The following L lines contain the numbers of the roads in ascending order.

Sample Input

4 5 1
1 2 1
1 3 4
2 3 1
2 4 3
3 4 1

Sample Output

4
1
3
4
5


这一题一开始我的思路是先在起点跑一次dijkstra,然后得到起点到达终点的最短路,依据这个最短路进行剪枝dfs搜到所有的边。
上代码(虽然爆栈了,不过还是发上来,提醒一下自己不这么写)

 1 #include <iostream>
 2 #include <set>
 3 #include <vector>
 4 #define Maxsize 10000 + 1
 5 #define INF 0x3fffffff
 6 using namespace std;
 7 struct node{
 8     int to;
 9     int dis;
10     int id;
11 };
12 int vertex,edge,max_extra;
13 int max_dis;
14 set<int> cnt;
15 vector<node> arr[Maxsize];
16 int d[Maxsize];
17 bool vis[Maxsize];
18 int help = INF;
19 bool dfs(int id,int sum){  // dfs返回一个布尔值,当最终搜到终点,说明之前的所有边都可以使用
20     if(sum > max_dis)return false; // 剪枝
21     else if(id == vertex){
22         if(sum <= help){
23             help = sum;
24             for(vector<node> :: iterator it = arr[id].begin(); it != arr[id].end(); it++){  // 有可能到达终点后继续跑,那样的边也是合理的
25                 if(dfs(it->to,sum + it->dis))
26                 cnt.insert(it->id);
27             }
28         }
29         return true;
30     }else{
31         int flag = 0;
32         for(vector<node> :: iterator it = arr[id].begin(); it != arr[id].end(); it++){
33             if(dfs(it->to,sum + it->dis)){
34                 flag = 1;
35                 cnt.insert(it->id);
36             }
37         }
38         if(flag)return true;
39         else return false;
40     }
41 }
42 int main(){
43     ios :: sync_with_stdio(false);
44     cin.tie(0);
45     cout.tie(0);
46     
47     cin >> vertex >> edge >> max_extra;
48     int from;
49     for(int i = 1; i <= edge; i++){
50         node a;
51         cin >> from >> a.to >> a.dis;
52         a.id = i;
53         arr[from].push_back(a);
54     }
55     
56     fill(d,d+Maxsize,INF);
57     fill(vis,vis+Maxsize,false);
58     d[1] = 0;
59     
60     for(int i = 0; i < vertex; i++){
61         int find_min = INF;
62         int find_v = -1;
63         for(int i = 1; i <= vertex; i++){
64             if(vis[i] == false && d[i] < find_min){
65                 find_min = d[i];
66                 find_v = i;
67             }
68         }
69         if(find_v == vertex)break;
70         vis[find_v] = true;
71         for(vector<node> :: iterator it = arr[find_v].begin(); it != arr[find_v].end(); it++){
72             if(d[it->to] > find_min + it->dis){
73                 d[it->to] = find_min + it->dis;
74             }
75         }
76     }
77     max_dis = d[vertex] + max_extra;
78     dfs(1,0);
79     cout<<cnt.size()<<endl;
80     for(set<int> :: iterator it = cnt.begin(); it != cnt.end(); it++){
81         cout<<*it<<endl;
82     }
83     return 0;
84 }

 

为什么会爆呢?其实数据量并没有题目写的那么大(顶多1e5),更多可能是因为每一条边都很短,导致递归过深了。

 

接下来是正解: 两边dijkstra,第一遍从起点跑,得出每一个顶点距离起点的最短距离。第二遍从终点跑,反向建图,得到每一个点到终点的最短距离。

这样一来,我们就可以知道任意一条边的两个端点分别距离起点和终点的最短距离了。

那么如何判断一个边是否可用呢? 只需要 写 d [ start - >left_vertex ]  +  edge_length + d[ right_vertex  -> end ]   <= d[ start -> end ]  + max_extra

一会上代码~

上一篇:1


下一篇:Feeding Vertex Shaders from Buffers从缓冲区给shader输入数据