zoukankan      html  css  js  c++  java
  • 【题解】 [HNOI2009] 最小圈 (01分数规划,二分答案,负环)

    题目背景

    如果你能提供题面或者题意简述,请直接在讨论区发帖,感谢你的贡献。

    题目描述

    对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除以k,现要求其中的最小值

    输入输出格式

    输入格式:

    第一行2个正整数,分别为n和m

    以下m行,每行3个数,表示边连接的信息,

    输出格式:

    一行一个数,表示最小圈的值,保留8位小数。

    输入输出样例

    输入样例#1: 复制
    4 5
    1 2 5
    2 3 5
    3 1 5
    2 4 3
    4 1 3
    输出样例#1: 复制
    3.66666667

    说明

    若设边权为vvv,那么n≤3000,m≤10000,v≤50000nle 3000,mle 10000,vle 50000n3000,m10000,v50000

    题解转自NaVi_Awson巨佬博客(快去访问 http://www.cnblogs.com/NaVi-Awson/p/7641518.html

    题解

    最小化平均值($01$分数规划)。

    使用二分求解。对于一个猜测的$mid$,只需判断是否存在平均值小于$mid$的回路。

    如何判断?

    假设存在一个包含$k$条边的回路,回路上各边权值为$w_1$ ,$w_2$ ,$...$,$w_k$ ,那么平均值小于$midv$意味着:

    $$w_1 +w_2 +...+w_k <k×mid$$

    即:

    $$(w_1 -mid)+(w_2 -mid)+...+(w_k -mid)<0$$

    换句话说,只要把边$(a,b)$的权$w(a,b)$改成$w(a,b)-mid$,再判断新图中是否有负环即可。

    存在负环,那么之前的不等式满足,即存在着更小的平均值,$r=mid$;不存在,$l=mid$。

    不要脸的贴自己的代码:

    //It is coded by Ning_Mew on 10.26
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    const double CDN=0.000000001;
    int n,m;
    double L,R,mid;
    double dist[10000+10];
    int head[3000+10],t;
    struct Edge{
        int next,to;
        double dis;
    }edge[10000+10];
    void add(int from,int to,double dis){
        edge[++t].next=head[from];
        edge[t].to=to;
        edge[t].dis=dis;
        head[from]=t;
    }
    bool vis[3000+10];
    void clear(){
        memset(vis,false,sizeof(vis));
        memset(dist,0,sizeof(dist));
        return;
    }
    bool SPFA(int u){
        vis[u]=true;
        for(int i=head[u];i!=0;i=edge[i].next){
            int v=edge[i].to;
            if(dist[v]>dist[u]+edge[i].dis-mid){
                if(vis[v]){vis[u]=false;return true;}
                dist[v]=dist[u]+edge[i].dis-mid;
                if(SPFA(v)){vis[u]=false;return true;}
            }
        }
        vis[u]=false;return false;
    }
    bool check(){
        clear();
        for(int i=1;i<=n;i++){if(SPFA(i))return true;}
        return false;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            R=max(R,(double)z);
        }
        while(L+CDN<R){
            mid=(L+R)/2;
            if(check()){R=mid;}
            else L=mid;
        }
        printf("%0.8lf",(L+R)/2);
        return 0;
    }
  • 相关阅读:
    思维科学的层次和学科构成
    知识管理--要对自己的知识做顶层的梳理和管理
    深入分析泛型编程--编译器使用模版生成代码
    算法与上下文
    深入理解递归算法
    什么是递归:递 与 归
    分治与”分析与综合”
    分治的逻辑学描述
    分治与递归
    generator的本质是将异步的管理剥离
  • 原文地址:https://www.cnblogs.com/Ning-Mew/p/7737582.html
Copyright © 2011-2022 走看看