[HAOI2006] 旅行

传送门:>HERE<

题意:给出一副无向图,求从S到T的任意一条路径中,最大边权与最小边权比值的最小值

解题思路:

  由于数据范围很小,可以考虑$O(m^2)$的算法。

  刚开始一直很困惑,不就跑一边求一个最大最小比一比吗,为什么还有最小值?太thick了,作为一个无向图,只要两点连通,路径可能多到不知哪里去了。而最大最小值是在同一条路径上的。那么如果Dfs呢,又太慢了。

  我们考虑,如果路径上的最小值已经确定了,那么只要找到剩余路径中最小的最大值就可以了。这好像让我们联想到了Kruscal算法——将所有边排序,枚举最小值(其实就是排序后一条一条枚举)。选定了每一条最小边以后,一条条将剩余的比它大的边插入,直到S和T连通为止——此时的边权一定是最小的最大值了。(单调)。这个过程用并查集就行了。

  再加上一点分数的处理。复杂度大约$O(m^2 log n)$

Code

/*By QiXingzhi*/
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef unsigned long long ll;
const int N = 510;
const int M = 5010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
struct Edge{ int u,v,w; }a[M];
struct Frac{ int x,y; }ans;
int n,m,x,y,z,S,T,par[N];
inline bool FracCompare(Frac _a, Frac _b){ return !(_a.x * _b.y < _b.x * _a.y); }
int gcd(int a, int b){ return !b ? a : gcd(b, a%b); }
inline void PrintAns(Frac __a){
    int g = gcd(__a.x, __a.y);
    __a.x /= g, __a.y /= g;
    if(__a.y == 1){ printf("%d",__a.x);
    else printf("%d/%d",__a.x, __a.y);
}
inline bool comp(const Edge& a, const Edge& b){ return a.w < b.w; }
int find(int x){ return par[x] == x ? x : par[x] = find(par[x]); }
inline void Try(int _t){
    for(int i = 1; i <= n; ++i) par[i] = i;
    int A,B,__max=-1;
    for(int i = _t; i <= m; ++i){
        A = find(a[i].u), B = find(a[i].v);
        if(A != B) par[A] = B;
        if(find(S) == find(T)){ __max = a[i].w; break; }
    }
    if(__max != -1){
        Frac _tmp1; _tmp1.x = __max, _tmp1.y = a[_t].w;
        if(FracCompare(_tmp1, ans) == 0) ans = _tmp1;
    }
}
inline void JudgeConnect(){
    int A,B;
    for(int i = 1; i <= n; ++i) par[i] = i;
    for(int i = 1; i <= m; ++i){
        A = find(a[i].u), B = find(a[i].v);
        if(A != B) par[A] = B;
    }
    if(find(S) != find(T)) printf("IMPOSSIBLE"), exit(0);
}
int main(){
    n = r, m = r;
    for(int i = 1; i <= m; ++i) a[i].u = r, a[i].v = r, a[i].w = r;
    S = r, T = r;
    sort(a+1, a+m+1, comp);
    JudgeConnect();
    ans.x = INF; ans.y = 1;
    for(int i = 1; i <= m; ++i) Try(i);
    PrintAns(ans);
    return 0;
}
原文地址:https://www.cnblogs.com/qixingzhi/p/9341575.html