• 【HNOI2009】 最小圈


    最小圈

    Preface 前言

    双(三)倍经验!

    第三题的博客qwq


    Problem model 问题模型

    这三道题都是给定一张有向图,首先判断是否有环

    如果有环就求出最大环或最小环的平均值

    解决这种题,我们通常都是使用 二分答案 & SPFA 判环求最短(长)路

    Train of thought 思路点拨

    • 为什么会想到二分答案呢?

    因为在找最大(小)环的时候我们一般会想到:

    遍历整个图,找到每一个环,然后算出它们的平均值,最后比较出最大(小)值

    但是题目可能是多组数据,还不说图的大小,所以这种思路大概率会 (T) 飞的

    • 那怎么做?

    ——不能找环,那我们就直接找答案啊!(这里要转换一下思想)

    因为找答案 (=) 枚举答案,而枚举答案一般使用较高效的二分答案

    • 再来转换一下关键式子

    我们能将求答案(ans)的式子表示如下:

    (ans=(len_1+len_2+len_3+···+len_k)/K)

    数学转换一下:

    (ans*K=len_1+len_2+len_3+···+len_k)

    移项一下:

    ((len_1+len_2+len_3+···+len_k)-ans*K=0)

    最后我们可得:

    ((len_1-ans)+(len_2-ans)+(len_3-ans)+···+(len_k-ans)≥0)

    (因为最开始的式子是整除,会有精度问题,所以是 (≥)

    • 经过上述转换之后,(SPFA)更新路径长度的写法就应该如下:

    dis[v]>dis[now]+e[i].val-middis[v]<dis[now]+e[i].val-mid

    dis[v]=dis[now]+e[i].val-mid

    其中 (mid) 是我们当前枚举的答案(第一句是最小环,第二句是最大环)

    • (check) 函数的写法也变得显而易见:

    每次枚举了 (ans),就跑 (SPFA) ,如果存在环则说明当前枚举的 (ans) 是合法的,反之不合法


    Code 代码

    注:这三道题我都写的 (dfs) 版的 (SPFA)

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,u,v,tot;
    double w,dis[520010];
    int vis[520010],head[520010];
    
    struct node {
        int to,net;
        double val;
    } e[520010];
    
    inline void add(int u,int v,double w) {
        e[++tot].to=v;
        e[tot].val=w;
        e[tot].net=head[u];
        head[u]=tot;
    }
    
    inline bool dfs(int now,double x) {
        vis[now]=1;
        for(register int i=head[now];i;i=e[i].net) {
            int v=e[i].to;
            if(dis[v]>dis[now]+e[i].val-x) {
                dis[v]=dis[now]+e[i].val-x;
                if(vis[v]==1||dfs(v,x)==true) return true;
            }
        }
        vis[now]=0;
        return false;
    }
    
    inline bool check(double x) {
        for(register int i=1;i<=n;i++) {
            vis[i]=0;
            dis[i]=20050206;
        }
        for(register int i=1;i<=n;i++) {
            if(dfs(i,x)==true) return true;
        }
        return false;
    }
    
    int main() {
        scanf("%d%d",&n,&m);
        for(register int i=1;i<=m;i++) {
            scanf("%d%d%lf",&u,&v,&w);
            add(u,v,w);
        }
        double l=-10000000,r=10000000;
        while(r-l>1e-10) {
            double mid=(l+r)/2;
            if(check(mid)==true) r=mid;
            else l=mid;
        }
        printf("%.8lf",l);
        return 0;
    }
    

    Epilogue 尾声

    最后,如果这篇题解有任何问题或不懂的地方,欢迎留言区评论

    我一定会及时回复、改正,谢谢各位dalao呀!orz


  • 相关阅读:
    2013.11.19上班 任务:写文档
    js 时间比较和货币格式显示
    SQL优化
    多线程消费队列中的接口数据,接口数据来源是kafka
    List<Map<String, Object>> 中根据某一个属性进行排序
    ES查询操作
    Valid Sudoku
    Decode Ways
    Jump Game
    Best Time to Buy and Sell Stock II
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13382056.html
Copyright © 2020-2023  润新知