zoukankan      html  css  js  c++  java
  • 【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


  • 相关阅读:
    .NET旋转PDF并保存旋转结果到文件
    C#的抽象类和接口的区别,在什么时候使用才合适?
    [转]SQL Server中多行多列连接成为单行单列
    VBS脚本COPY指定日期文件及文件夹
    eval同时绑定两个值:通过String.Format给超链接中的两个参数赋值
    How to recover SA password on Microsoft SQL Server 2008 R2
    [转]asp.net 前台绑定后台变量方法总结:<%= %> 和<%# %>的区别
    Js得到radiobuttonlist选中值的两种方式
    Ameriscan增加一个新的client:AP_CONSO
    命令行处理pdf的利器:PDFTK.exe
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13382056.html
Copyright © 2011-2022 走看看