题目描述 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行以后)

 /**
* codevs.cn
* Problem1001
* Accepted
* Time:378ms
* Memory:588k
*/
#include<iostream>
#include<sstream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
typedef bool boolean;
#define smin(a, b) (a) = min((a), (b))
#define smax(a, b) (a) = max((a), (b))
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
aFlag = -;
x = getchar();
}
for(u = x - ''; isdigit((x = getchar())); u = u * + x - '');
ungetc(x, stdin);
u *= aFlag;
} typedef class union_found{
public:
int points;
int *f;
union_found():f(NULL) {}
union_found(int points):points(points){
f = new int[(const int)(points + )];
for(int i = ; i <= points; i++)
f[i] = i;
}
int find(int x) {
if(f[x] != x) return f[x] = find(f[x]);
return f[x];
}
void unit(int fa, int so) {
int ffa = find(fa);
int fso = find(so);
f[fso] = ffa;
}
boolean connected(int a, int b) {
return find(a) == find(b);
}
void clean(){
for(int i = ; i <= points; i++)
f[i] = i;
}
}union_found; ///map template starts
typedef class Edge{
public:
int end;
int next;
int w;
int id;
Edge(const int end = , const int next = , const int w = , const int id = ):end(end), next(next), w(w), id(id){}
}Edge; typedef class MapManager{
public:
int ce;
int *h;
Edge *edge;
MapManager(){}
MapManager(int points, int limit):ce(){
h = new int[(const int)(points + )];
edge = new Edge[(const int)(limit + )];
memset(h, , sizeof(int) * (points + ));
}
inline void addEdge(int from, int end, int w, int id){
edge[++ce] = Edge(end, h[from], w, id);
h[from] = ce;
}
inline void addDoubleEdge(int from, int end, int w, int id){
addEdge(from, end, w, id);
addEdge(end, from, w, id);
}
inline void clean(){
delete[] h;
delete[] edge;
ce = ;
}
}MapManager; #define m_begin(g, i) (g).h[(i)]
#define m_end(g, i) (g).edge[(i)].end
#define m_next(g, i) (g).edge[(i)].next
#define m_w(g, i) (g).edge[(i)].w
///map template ends typedef class Fraction{
public:
int s;
int m;
Fraction():s(),m(){}
Fraction(int s,int m){
int g = getCommon(s, m);
this->s = s / g;
this->m = m / g;
}
boolean empty(){
return m == ;
}
boolean operator <(Fraction another) const{
if(another.empty()) return true;
if(m == ) return false;
if(this->s == another.s) return this->m>another.m;
return (this->s * 1.0 / this->m) < (another.s * 1.0 / another.m);
}
private:
int getCommon(int a, int b){
if(b == ) return a;
return getCommon(b, a % b);
}
}Fraction; typedef class Edge1{
public:
int end;
int from;
int w;
Edge1(const int end = , const int from = , const int w = ):end(end), from(from), w(w){}
}Edge1; int n, m;
Edge1* edge;
union_found uf;
MapManager g;
int s, t; inline boolean cmpare(const Edge1& a, const Edge1& b){
return a.w > b.w;
} inline void init(){
readInteger(n);
readInteger(m);
edge = new Edge1[(const int)(m + )];
for(int i = ; i <= m; i++){
readInteger(edge[i].from);
readInteger(edge[i].end);
readInteger(edge[i].w);
}
readInteger(s);
readInteger(t);
} int minl, maxl, maxide;
void dfs(int minv, int maxv, int maxid, int last, int node){
if(node == t){
minl = minv, maxl = maxv, maxide = maxid;
return;
}
for(int i = m_begin(g, node); i != ; i = m_next(g, i)){
int& e = m_end(g, i);
if(e == last) continue;
int nmin = min(minv, m_w(g, i));
int nmax = max(maxv, m_w(g, i));
int nid = (nmax == m_w(g, i)) ? (g.edge[i].id) : (maxid);
dfs(nmin, nmax, nid, node, e);
}
} boolean *enable;
Fraction result;
inline void solve(){
sort(edge + , edge + m + , cmpare);
enable = new boolean[(const int)(m + )];
memset(enable, true, sizeof(boolean) * (m + ));
uf = union_found(n);
for(int i = ; i <= m; i++){
int j;
g = MapManager(n, m * );
for(j = ; j <= m; j++){
if(enable[j] && !uf.connected(edge[j].from, edge[j].end)){
uf.unit(edge[j].from, edge[j].end);
g.addDoubleEdge(edge[j].from, edge[j].end, edge[j].w, j);
if(uf.connected(s, t)){
maxide = -;
dfs(0x7fffffff, -, -, , s);
uf.clean();
g.clean();
break;
}
}
}
if(j == m + ) break;
else{
smin(result, Fraction(maxl, minl));
enable[maxide] = false;
}
}
if(result.empty()) printf("IMPOSSIBLE");
else if(result.m != ) printf("%d/%d", result.s, result.m);
else printf("%d", result.s);
} int main(){
init();
solve();
return ;
}

后话

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

codevs1001 舒适的路线 - 贪心 - 并查集的更多相关文章

  1. CODEVS1001 舒适的路线 (并查集)

    对所有边从大到小排序,枚举最大边,O(m)验证,用并查集维护图是否联通. program CODEVS1001; ; maxn=; INF=; type arr=record u,v,w:int64; ...

  2. codevs 1001 舒适的路线 (并查集)

    题目描述 Description Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光. Z小镇附近共有 N(<N≤)个景点(编号为1,,,…,N),这些景点被M(<M≤)条道路连 ...

  3. [codevs1001]舒适的路线

    [codevs1001]舒适的路线 试题描述 Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光.Z小镇附近共有N(1<N≤500)个景点(编号为1,2,3,-,N),这些景点被M(0 ...

  4. POJ 1456 Supermarket(贪心+并查集)

    题目链接:http://poj.org/problem?id=1456 题目大意:有n件商品,每件商品都有它的价值和截止售卖日期(超过这个日期就不能再卖了).卖一件商品消耗一个单位时间,售卖顺序是可以 ...

  5. [题解]codevs1001 舒适的路线

    h3 { font-family: Consolas; color: #339966 } .math { font-family: Consolas; color: gray } 题目描述 Descr ...

  6. poj1456(贪心+并查集)

    题目链接: http://poj.org/problem?id=1456 题意: 有n个商品, 已知每个商品的价格和销售截止日期, 每销售一件商品需要花费一天, 即一天只能销售一件商品, 问最多能买多 ...

  7. Codeforces 437D The Child and Zoo(贪心+并查集)

    题目链接:Codeforces 437D The Child and Zoo 题目大意:小孩子去參观动物园,动物园分非常多个区,每一个区有若干种动物,拥有的动物种数作为该区的权值.然后有m条路,每条路 ...

  8. POJ - 1456 贪心+并查集

    做法一:直接贪心,按照利润排序,然后直接尽量给每个活动安排到最晚的时间即可.时间复杂度O(n * d)当d都为10000时,很容易超时.由于这题数据比较水,所有贪心未超时. AC代码 #include ...

  9. poj1456 Supermarket 贪心+并查集

    题目链接:http://poj.org/problem?id=1456 题意:有n个物品(0 <= n <= 10000) ,每个物品有一个价格pi和一个保质期di (1 <= pi ...

随机推荐

  1. tcpdump 学习

    简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的 ...

  2. linux rz sz的安装

    可以使用yum来安装 yum -y install lrzsz 然后rz就是上传文件,sz就是把文件导到本地.sz 文件名  即可

  3. QQ公众号?是的,你没看错!

    微信公众平台培育了800多万的微信公众号,自身也通过微信游戏.广告分销等找到了一些增值盈利模式.作为同门大师兄,qq也在11月份推出了QQ公众号,第一个手机QQ上的“生活服务号”——YTO圆通速递上线 ...

  4. 【Espruino】NO.07 获取电压值

    http://blog.csdn.net/qwert1213131/article/details/27985645 本文属于个人理解,能力有限,纰漏在所难免.还望指正! [小鱼有点电] 前几节的内容 ...

  5. URAL 1517 Freedom of Choice (后缀数组 输出两个串最长公共子串)

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/whyorwhnt/article/details/34075603 题意:给出两个串的长度(一样长) ...

  6. UIWebview于JS交互

    最近使用火车票网的限行网页,但是广告以及头部nav和地步footer都是我们现在所不需要的,所以决定使用js交互,下面所有代码都写在 webViewDidFinishLoad 里面 1.查看原网址的源 ...

  7. ppt插入声音

    1:点击插入>音频>文件中的音频 2:插入成功后,会出现一个声音的图表 3:对播放格式进行设置,设置循环播放等. 4:双击对声音进行编辑 ,会出现右边的各个组件, 5:点击下拉框>效 ...

  8. jquery closest & parent比较

    .closest() .parents() 从当前元素开始 从父元素开始 沿 DOM 树向上遍历,直到找到已应用选择器的一个匹配为止. 沿 DOM 树向上遍历,直到文档的根元素为止,将每个祖先元素添加 ...

  9. iOS设计规范HIG

    点击图标大小至少为这么大: Make it easy for people to interact with content and controls by giving each interacti ...

  10. nodejs 将网上的图片下载到本地文件

    var request = require('request'); var fs = require('fs'); var img_src = 'https://www.baidu.com/img/b ...