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

一会上代码~

原文地址:https://www.cnblogs.com/popodynasty/p/12194914.html

时间: 2024-11-02 05:11:42

VJ - H - Almost the shortest route - 图论(反向建图)的相关文章

HDU 2680 Choose the best route &lt;SPFA算法+反向建图&gt;

Choose the best route Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10690    Accepted Submission(s): 3454 Problem Description One day , Kiki wants to visit one of her friends. As she is liabl

poj1122 FDNY to the Rescue!(dij+反向建图+输出路径)

题目链接:poj1122 FDNY to the Rescue! 题意:给出矩阵,矩阵中每个元素tij表示从第i个交叉路口到第j个交叉路口所需时间,若tij为-1则表示两交叉路口之间没有直接路径,再给出火警位置所在的交叉路口 和 一个或多个消防站所处的交叉路口位置.输出要求按消防站到火警位置所需时间从小到大排列,输出信息包括消防站位置(初始位置),火警位置(目标位置),所需时间,最短路径上每个交叉路口. 题解:反向建图,从火警位置求一次最短路,求最短路时记录路径,按时间从小到大输出. 1 #in

HDU2647(拓扑排序+反向建图)

题意不说了,说下思路. 给出的关系是a要求的工资要比b的工资多,由于尽可能的让老板少付钱,那么a的工资就是b的工资+1,可以确定关系为a>b,根据拓扑排序建边的原则是把"小于"关系看成有向边,那么我们可以建边v->u. #include <stdio.h> #include <string.h> #include <string> #include <iostream> #include <algorithm> #

HDU1535Invitation Cards(有向图,正向建图和反向建图各spfa一次)

Invitation Cards Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2374    Accepted Submission(s): 1151 Problem Description In the age of television, not many people attend theater performances.

HDU4857——逃生(反向建图+拓扑排序)

逃生 Description 糟糕的事情发生啦,现在大家都忙着逃命.但是逃命的通道很窄,大家只能排成一行. 现在有n个人,从1标号到n.同时有一些奇怪的约束条件,每个都形如:a必须在b之前.同时,社会是不平等的,这些人有的穷有的富.1号最富,2号第二富,以此类推.有钱人就贿赂负责人,所以他们有一些好处.负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推.那么你就要安排大家的顺序.我们保证一

[CF825E] Minimal Labels(反向建图,拓扑排序)

题目链接:http://codeforces.com/problemset/problem/825/E 题意:给一个有向图,求一个排列,这个排列是每一个点的序号,使得序号对应的点的排序符合拓扑序并且这个排列字典序最小. 直接跑字典序最小的拓扑排序是不行的,因为那样只是确保点的字典序而非这个排列的字典序,比如这个数据: 10 15 2 反过来考虑,点号大的入度为0的点一定排在后面,这个位置确定了.但是点好小的入度为0的未必一定排在前面,因为这个点之前可能有入度不为0,但是与此点无关的点在前面,按题

hdu3499(分层图最短路 or 反向建图)

传送门 方法一:分层图 #include<bits/stdc++.h> #define per(i,a,b) for(int i=a;i<=b;i++) #define mod 1000000007 using namespace std; typedef long long ll; const ll inf =23333333333333333LL; const double eps=1e-8; int T; int read(){ char ch=getchar(); int res

POJ 3687 反向建图+拓扑

Labeling Balls Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11146   Accepted: 3192 Description Windy has N balls of distinct weights from 1 unit to N units. Now he tries to label them with 1 to N in such a way that: No two balls share

HDU 3639 Hawk-and-Chicken(强连通缩点+反向建图)

http://acm.hdu.edu.cn/showproblem.php?pid=3639 题意: 有一群孩子正在玩老鹰抓小鸡,由于想当老鹰的人不少,孩子们通过投票的方式产生,但是投票有这么一条规则:投票具有传递性,A支持B,B支持C,那么C获得2票(A.B共两票),输出最多能获得的票数是多少张和获得最多票数的人是谁? 思路: 先强连通缩点反向建图,在计算强连通的时候,需要保存每个连通分支的结点个数. 为什么要反向建图呢?因为要寻找票数最多的,那么肯定是入度为0的点,然后dfs计算它的子节点的