[BZOJ1050][HAOI2006]旅行comf(并查集)

题目描述

传送门

题解

判断是否连通很简单,直接上ufs就可以了。
但是接下来的做法我有过一些错误的想法。一看上去觉得这题很像刚做过的最优贸易那道题,所以一开始用spfa维护了一坨最大值和最小值。但是这样实际上是不可行的。因为如果要使最大值与最小值的比最小的话,就是要让最大值尽可能小,最小值尽可能大。如果维护了这两个量的话,一组合原先的最小值就有可能变成大的。这样就很不科学了。
正确的做法应该这样思考:从目标考虑,最大值与最小值的比最小,如果我们现在确定了最小值的话,那么整条路线上的最大值应该是越小越好。那么我们可以枚举最小值,然后在图中求能使s和t连通的最小的最大值。假设说最小值为min,最大值为max,将边权在min~max之间的边全部加到图里,如果s和t连通了,则这是一个合法的方案。
那么这就是一个很科学的做法了!先将所有的边排序,然后枚举图中的最小值,从最小值的那条边起,依次向图中加边,知道s和t联通。那么此时的最小值和最大值就是一组合法的方案,更新就可以了。
判断连通什么的用ufs维护一下。时间复杂度 O(m2α)

代码

#include
#include
#include
#include
#include
using namespace std;
#define N 505
#define M 5005
#define INF 30000

int n,m,x,y,z,s,t,up,down;
double ans;
int f[N];
struct hp{int x,y,z;}edge[M];

int cmp(hp a,hp b)
{
    return a.zint find(int x)
{
    if (x==f[x]) return x;
    f[x]=find(f[x]);
    return f[x];
}
int gcd(int a,int b)
{
    if (!b) return a;
    else return gcd(b,a%b);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) f[i]=i;
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&x,&y,&z);
        int f1=find(x),f2=find(y);
        if (f1!=f2) f[f1]=f2;
        edge[i].x=x,edge[i].y=y,edge[i].z=z;
    }
    scanf("%d%d",&s,&t);
    if (find(s)!=find(t))
    {
        puts("IMPOSSIBLE");
        return 0;
    }

    ans=INF;
    sort(edge+1,edge+m+1,cmp);
    for (int l=1;l<=m;++l)
    {
        bool flag=false; int r;
        for (int i=1;i<=n;++i) f[i]=i;
        for (int i=l;i<=m;++i)
        {
            int f1=find(edge[i].x),f2=find(edge[i].y);
            if (f1!=f2) f[f1]=f2;
            if (find(s)==find(t))
            {
                r=i;
                flag=true;
                break;
            }
        }
        if (flag)
        {
            double now=(edge[r].z+0.0)/(edge[l].z+0.0);
            if (nowint t=gcd(up,down);
    if (t!=down) printf("%d/%d\n",up/t,down/t);
    else printf("%d\n",up/t);
}

总结

①以后要好好读题。想出做法了之后算算样例再写。
②要求两个量的情况要注意一种思路:枚举一个量然后求另一个量。非常显然但是也十分容易忽略。

你可能感兴趣的:(题解,并查集,省选)