zoukankan      html  css  js  c++  java
  • [BZOJ]1050 旅行comf(HAOI2006)

      图论一直是小C的弱项,相比其它题型,图论的花样通常会更多一点,套路也更难捉摸。

    Description

      给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000)。给你两个顶点S和T,求一条路径,使得路径上最大边和最小边的比值最小。如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出这个比值,如果需要,表示成一个既约分数。 备注: 两个顶点之间可能有多条路径。

    Input

      第一行包含两个正整数,N和M。下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

    Output

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

    Sample Input

      3 3
      1 2 10
      1 2 5
      2 3 8
      1 3

    Sample Output

      5/4

    HINT

      1<N<=500,1<=x,y<=N,0<v<30000,0<M<=5000

    Solution

      看到最大和最小值我们就可以感觉到肯定不能直接走,然后很自然地想到了排序。

      这种图论的题型常常要求我们逆向思考,我们不是找一条路径去更新答案,而是枚举一个答案看它能否构成路径。

      于是我们枚举路径中的最小的那条边,从小到大加边,直到S和T连通,

      说明存在一条S到T的路径。用最后加入的这条边和枚举的最小边更新答案。

      判连通性用并查集即可。

      时间复杂度O(m^2)

      在黄学长博客里还看到一个比较妙的解法分享一下:

        

      理论复杂度是一样的,但实际远小于上界。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define MN 505
    #define MM 5005
    #define INF 0x3FFFFFFF
    using namespace std;
    struct edge{int x,y,z;}b[MM];
    int fa[MN];
    int n,m,S,T,ans1,ans2,g;
    
    inline int read()
    {
        int n=0,f=1; char c=getchar();
        while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
        while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
        return n*f;
    }
    
    int getf(int x) {return fa[x]?fa[x]=getf(fa[x]):x;}
    inline void gather(int x,int y) {if (x!=y) fa[x]=y;}
    int gcd(int x,int y) {return y?gcd(y,x%y):x;}
    bool cmp(const edge& a,const edge& b) {return a.z<b.z;}
    
    int main()
    {
        register int i,j;
        n=read(); m=read();
        for (i=1;i<=m;++i)
            b[i].x=read(),b[i].y=read(),b[i].z=read();
        S=read(); T=read();
        sort(b+1,b+m+1,cmp);
        ans1=INF; ans2=1;
        for (i=1;i<=m;++i)
        {
            memset(fa,0,sizeof(fa));
            for (j=i;j<=m;++j)
            {
                gather(getf(b[j].x),getf(b[j].y));
                if (getf(S)==getf(T)) break;
            }
            if (j>m) break;
            if ((double)b[j].z/b[i].z<(double)ans1/ans2) ans1=b[j].z,ans2=b[i].z;
        }
        if (i==1) return 0*printf("IMPOSSIBLE");
        g=gcd(ans1,ans2);
        ans1/=g; ans2/=g;
        if (ans2==1) printf("%d",ans1); else printf("%d/%d",ans1,ans2);
    }

    Last Word

      用并查集解决图论的问题也是常见的思路。

      小C会告诉你迷之RE是因为并查集写错了?

  • 相关阅读:
    软工实践总结
    Beta总结
    beta冲刺6/7
    beta冲刺5/7
    Beta冲刺4/7
    beta冲刺3/7
    beta冲刺2/7
    beta冲刺1/7
    答辩总结
    ES6中的块级作用域与函数声明
  • 原文地址:https://www.cnblogs.com/ACMLCZH/p/7424407.html
Copyright © 2011-2022 走看看