codevs1001 舒适的路线 - 贪心 - 并查集
题目描述 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连通。连通后可以发现当经过这条路上最大限速的这一条边的情况是最优的(因为这条路径上每一条边都尽可能大),但是不是整张图中最优的。怎么办?多算几条很优的线。把这条路径最大限速的那条边删掉(因为再保留这条边已经没有意义了,对于这条边来说已经是最优的了),更新答案,重新最大生成树。直到无论怎么加边s,t都不会连通的时候退出算法。
(貌似用最小生成树也行,不过删的那条边变成了最小限速),算法时间复杂度大概是O(m2 + mn)(生成树上dfs,时间复杂度$O(n)$)
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连通,后者是最大的限速。还有个方法优化,把没有的边去掉,而不是一次一次地删最大的那条边,详见[传送门]