zoukankan      html  css  js  c++  java
  • BZOJ1486 最小圈 [分数规划+负权环]

    Description

    考虑带权的有向图(G=(V,E))以及(w:E ightarrow R),每条边(e=(i,j)(i eq j,iin V,jin V))的权
    值定义为(w_{i,j}),令(n=|V|)(c=(c_1,c_2,cdots,c_k)(c_iin V))(G)中的一个圈当且仅当
    ((ci,ci+1)(1≤i<k))((ck,c1))都在(E)中,这时称(k)为圈(c)的长度同时令(c_{k+1}=c_1),并定义圈(c=(c_1,c_2,cdots,c_k))的平均值为(mu(c)=sumlimits_{i=1}^{k} w_{c_i,c_{i+1}}/k),即(c)上所有边的权值的平均值。令(mu'(c)=Min(mu(c)))(G)中所有圈(c)的平均值的最小值。现在的目标是:在给定了一个图(G=(V,E))以及(w:E ightarrow R)之后,请求出(G)中所有圈(c)的平均值的最小值(mu'(c)=Min(mu(c)))

    Input

    第一行2个正整数,分别为(n)(m),并用一个空格隔开,只用(n=|V|,m=|E|)分别表示图中有(n)个点(m)条边。 接下来m行,每行3个数(i,j,w_{i,j}),表示有一条边((i,j))且该边的权值为(w_{i,j})。输入数据保证图(G=(V,E))连通,存在圈且有一个点能到达其他所有点。

    Output

    请输出一个实数(mu'(c)=Min(mu(c))),要求输出到小数点后8位。

    Sample Input

    4 5
    1 2 5
    2 3 5
    3 1 5
    2 4 3
    4 1 3

    Sample Output

    3.66666667

    Hint

    对于100%的数据,(nle 3000,mle 10000,|w_{i,j}| le 10^7)

    思路

    看到题目中的“平均值的最小值”,便想到可以分数规划,这道题运用的是0-1分数规划的思想。

    0-1分数规划的一般形式是这样一个式子:
    (r=(∑(c_i*x_i))/(∑(d_i*x_i))) 在其中 (x_i∈){0,1}

    我们一般求的便是r的最值。有一种基础的方法是二分r的值:
    将原式变形,(∑(c_i*x_i))-(∑(d_i*x_i)*r=0)
    (f(r)=∑(c_i*x_i))-(∑(d_i*x_i)*r)

    不难看出(f(r))是单调递减的函数,我们可以通过(f(r))与0的关系来二分出r的最值:
    (f(r)>0)时 表明这时r可以取更大
    (f(r)=0)时 表明这时r即为符合要求的最值
    (f(r)<0)时 表明这时r可以取更小

    我们便在本题中运用这个思想。在本题中,公式中的r即为最小的平均值,c即为边权,d为1 (∑(d_i*x_i)即为边的个数),x表示这条边是否在圈中,f(r)转换后可以看作是边权减去r后最小圈的权值之和。

    当f(r)<0时,这个圈就是负权环,于是我们便可以根据是否存在负权环来二分出r的值。
    我判断负权环的方法是使用dfs-spfa,详细内容可以看代码(顺便给出模板题链接洛谷P3385)。

    代码

    需要注意的细节:

    1. 注意二分的精度
    2. 判负环注意方法,不然容易T
    #include <bits/stdc++.h>
    #define exp 1e-9  //二分的精度
    #define inf 1e9
    #define MAXN 10005
    using namespace std;
    int n,m,a[MAXN],b[MAXN];
    int cnt,head[MAXN],vis[MAXN],flag;
    double dis[MAXN],c[MAXN]; //一定要用double存
    struct Edge{int to,next; double w;} edge[MAXN];
    
    void addedge(int x, int y, double w)
    { //前向星存图
        edge[++cnt].next=head[x];
        edge[cnt].to=y;
        edge[cnt].w=w;
        head[x]=cnt;
    }
    
    void spfa(int x)
    {  //dfs_spfa判负环
        vis[x]=true;
        for(int i=head[x]; i; i=edge[i].next)
        {
            int to=edge[i].to;
            if(dis[x]+edge[i].w<dis[to])
            {
                dis[to]=dis[x]+edge[i].w;
                if(vis[to]) {flag=true; return;}
                else spfa(to);
            }
        }
        vis[x]=false;
    }
    
    bool check(double x)
    {
        cnt=flag=0;
        memset(head, 0, sizeof(head));
        memset(dis, 127/3, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        for(int i=1; i<=m; i++) addedge(a[i], b[i], c[i]-x); //重新赋权值
        for(int i=1; i<=n; i++)
        {
            spfa(i); //判负环
            if(flag) return 1;	
        }
        return 0;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=m; i++) scanf("%d%d%lf",&a[i],&b[i],&c[i]);
        double l=-inf,r=inf;
        while(l+exp<r) //分数规划
        {
            double mid=(l+r)/2;
            if(check(mid)) r=mid;
            else l=mid;
        }
        printf("%.8lf",l);
        return 0;
    } 
    
  • 相关阅读:
    WQS二分
    题解 洛谷 P4696 【[CEOI2011]Matching】
    原根
    单位根反演
    题解 洛谷 P4218 【[CTSC2010]珠宝商】
    题解 洛谷 P5434 【有标号荒漠计数】
    题解 洛谷 P5406 【[THUPC2019]找树】
    题解 洛谷 P3563 【[POI2013]POL-Polarization】
    题解 洛谷 P6078 【[CEOI2004]糖果】
    拉格朗日插值法
  • 原文地址:https://www.cnblogs.com/CrazyDave/p/8465383.html
Copyright © 2011-2022 走看看