首页 > 代码库 > [题解]codevs1001 舒适的路线

[题解]codevs1001 舒适的路线

<style>h3 { font-family: Consolas; color: #339966 } .math { font-family: Consolas; color: gray }</style>

题目描述 Description

  Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。Z小镇附近共有N(1<N≤500)个景点(编号为1,2,3,…,N),这些景点被M(0<M≤5000)条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。频繁的改变速度使得游客们很不舒服,因此大家从一个景点前往另一个景点的时候,都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。

输入描述 Input Description

第一行包含两个正整数,N和M。
接下来的M行每行包含三个正整数:x,y和v(1≤x,y≤N,0 最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

输出描述 Output Description

如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。

样例输入 Sample Input

样例1
4 2
1 2 1
3 4 2
1 4

样例2
3 3
1 2 10
1 2 5
2 3 8
1 3

样例3
3 2
1 2 2
2 3 4
1 3

样例输出 Sample Output

样例1
IMPOSSIBLE

样例2
5/4

样例3
2

数据范围及提示 Data Size & Hint

N(1<N≤500)

M(0<M≤5000)

Vi在int范围内

(转自[codevs1001])


   最开始(大概半年前吧),这道题也没想出来,直接去spfa,写到一半,发现有问题,一个点最大限速比最小限速最小,不能保证后面的点也是最小。

然后看题解,看了几个,都没看懂,只是知道了一件事,要用并查集。

  现在重新来看这道题。通过并查集想到了最小(大)生成树。按照最大生成树来做,直到s和t连通。连通后可以发现当经过这条路上最大限速的这一条边的情况是最优的(因为这条路径上每一条边都尽可能大),但是不是整张图中最优的。怎么办?多算几条很优的线。把这条路径最大限速的那条边删掉(因为再保留这条边已经没有意义了,对于这条边来说已经是最优的了),更新答案(dfs一遍完成,数据较弱所以不会超时),重新最大生成树。直到无论怎么加边s,t都不会连通的时候退出算法。

(貌似用最小生成树也行,不过删的那条边变成了最小限速),算法时间复杂度大概是O(m2 + mn)

Code(超级不简洁的代码,其中前134行是模板,主要过程在167行以后)

  1 /**
  2  * codevs.cn
  3  * Problem1001
  4  * Accepted
  5  * Time:378ms
  6  * Memory:588k
  7  */ 
  8 #include<iostream>
  9 #include<sstream>
 10 #include<cstdio>
 11 #include<cmath>
 12 #include<cstdlib>
 13 #include<cstring>
 14 #include<cctype>
 15 #include<ctime>
 16 #include<queue>
 17 #include<set>
 18 #include<map>
 19 #include<stack>
 20 #include<vector>
 21 #include<algorithm>
 22 using namespace std;
 23 typedef bool boolean;
 24 #define smin(a, b) (a) = min((a), (b))
 25 #define smax(a, b) (a) = max((a), (b))
 26 template<typename T>
 27 inline void readInteger(T& u){
 28     char x;
 29     int aFlag = 1;
 30     while(!isdigit((x = getchar())) && x != -);
 31     if(x == -){
 32         aFlag = -1;
 33         x = getchar();
 34     }
 35     for(u = x - 0; isdigit((x = getchar())); u = u * 10 + x - 0);
 36     ungetc(x, stdin);
 37     u *= aFlag;
 38 }
 39 
 40 typedef class union_found{
 41     public:
 42         int points;
 43         int *f;
 44         union_found():f(NULL) {}
 45         union_found(int points):points(points){
 46             f = new int[(const int)(points + 1)];
 47             for(int i = 0; i <= points; i++)
 48                 f[i] = i;
 49         }
 50         int find(int x) {
 51             if(f[x] != x)    return f[x] = find(f[x]);
 52             return f[x];
 53         }
 54         void unit(int fa, int so) {
 55             int ffa = find(fa);
 56             int fso = find(so);
 57             f[fso] = ffa;
 58         }
 59         boolean connected(int a, int b) {
 60             return find(a) == find(b);
 61         }
 62         void clean(){
 63             for(int i = 0; i <= points; i++)
 64                 f[i] = i;
 65         }
 66 }union_found;
 67 
 68 ///map template starts
 69 typedef class Edge{
 70     public:
 71         int end;
 72         int next;
 73         int w;
 74         int id;
 75         Edge(const int end = 0, const int next = 0, const int w = 0, const int id = 0):end(end), next(next), w(w), id(id){}
 76 }Edge;
 77 
 78 typedef class MapManager{
 79     public:
 80         int ce;
 81         int *h;
 82         Edge *edge;
 83         MapManager(){}
 84         MapManager(int points, int limit):ce(0){
 85             h = new int[(const int)(points + 1)];
 86             edge = new Edge[(const int)(limit + 1)];
 87             memset(h, 0, sizeof(int) * (points + 1));
 88         }
 89         inline void addEdge(int from, int end, int w, int id){
 90             edge[++ce] = Edge(end, h[from], w, id);
 91             h[from] = ce;
 92         }
 93         inline void addDoubleEdge(int from, int end, int w, int id){
 94             addEdge(from, end, w, id);
 95             addEdge(end, from, w, id);
 96         }
 97         inline void clean(){
 98             delete[] h;
 99             delete[] edge;
100             ce = 0;
101         }
102 }MapManager;
103 
104 #define m_begin(g, i) (g).h[(i)]
105 #define m_end(g, i) (g).edge[(i)].end
106 #define m_next(g, i) (g).edge[(i)].next
107 #define m_w(g, i) (g).edge[(i)].w
108 ///map template ends
109 
110 typedef class Fraction{
111     public:
112         int s;
113         int m;
114         Fraction():s(0),m(0){}
115         Fraction(int s,int m){
116             int g = getCommon(s, m);
117             this->s = s / g;
118             this->m = m / g;
119         }
120         boolean empty(){
121             return m == 0;
122         }
123         boolean operator <(Fraction another) const{
124             if(another.empty())    return true;
125             if(m == 0)    return false;
126             if(this->s == another.s) return this->m>another.m;
127             return (this->s * 1.0 / this->m) < (another.s * 1.0 / another.m);
128         }
129     private:
130         int getCommon(int a, int b){
131             if(b == 0) return a;
132             return getCommon(b, a % b);
133         }
134 }Fraction;
135 
136 typedef class Edge1{
137     public:
138         int end;
139         int from;
140         int w;
141         Edge1(const int end = 0, const int from = 0, const int w = 0):end(end), from(from), w(w){}
142 }Edge1;
143 
144 int n, m;
145 Edge1* edge;
146 union_found uf;
147 MapManager g;
148 int s, t;
149 
150 inline boolean cmpare(const Edge1& a, const Edge1& b){
151     return a.w > b.w;
152 }
153 
154 inline void init(){
155     readInteger(n);
156     readInteger(m);
157     edge = new Edge1[(const int)(m + 1)];
158     for(int i = 1; i <= m; i++){
159         readInteger(edge[i].from);
160         readInteger(edge[i].end);
161         readInteger(edge[i].w);
162     }
163     readInteger(s);
164     readInteger(t);
165 }
166 
167 int minl, maxl, maxide;
168 void dfs(int minv, int maxv, int maxid, int last, int node){
169     if(node == t){
170         minl = minv, maxl = maxv, maxide = maxid;
171         return;
172     }
173     for(int i = m_begin(g, node); i != 0; i = m_next(g, i)){
174         int& e = m_end(g, i);
175         if(e == last)    continue;
176         int nmin = min(minv, m_w(g, i));
177         int nmax = max(maxv, m_w(g, i));
178         int nid = (nmax == m_w(g, i)) ? (g.edge[i].id) : (maxid);
179         dfs(nmin, nmax, nid, node, e);
180     }
181 }
182 
183 boolean *enable;
184 Fraction result;
185 inline void solve(){
186     sort(edge + 1, edge + m + 1, cmpare);
187     enable = new boolean[(const int)(m + 1)];
188     memset(enable, true, sizeof(boolean) * (m + 1));
189     uf = union_found(n);
190     for(int i = 1; i <= m; i++){
191         int j;
192         g = MapManager(n, m * 2);
193         for(j = 1; j <= m; j++){
194             if(enable[j] && !uf.connected(edge[j].from, edge[j].end)){
195                 uf.unit(edge[j].from, edge[j].end);
196                 g.addDoubleEdge(edge[j].from, edge[j].end, edge[j].w, j);
197                 if(uf.connected(s, t)){
198                     maxide = -1;
199                     dfs(0x7fffffff, -1, -1, 0, s);
200                     uf.clean();
201                     g.clean();
202                     break;
203                 }
204             }
205         }
206         if(j == m + 1)    break;
207         else{
208             smin(result, Fraction(maxl, minl));
209             enable[maxide] = false;
210         }
211     }
212     if(result.empty())    printf("IMPOSSIBLE");
213     else if(result.m != 1)    printf("%d/%d", result.s, result.m);
214     else printf("%d", result.s);
215 }
216 
217 int main(){
218     init();
219     solve();
220     return 0;
221 }

 

[后记]

   最后加的那一条边是最小的限速,可以倒推,得到一条边使s, t连通,后者是最大的限速。还有个方法优化,把没有的边去掉,而不是一次一次地删最大的那条边,详见[传送门]

[题解]codevs1001 舒适的路线