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;
    } 
    
  • 相关阅读:
    array and ram
    char as int
    pointer of 2d array and address
    Install SAP HANA EXPRESS on Google Cloud Platform
    Ubuntu remount hard drive
    Compile OpenSSL with Visual Studio 2019
    Install Jupyter notebook and tensorflow on Ubuntu 18.04
    Build OpenCV text(OCR) module on windows with Visual Studio 2019
    Reinstall VirtualBox 6.0 on Ubuntu 18.04
    Pitfall in std::vector<cv::Mat>
  • 原文地址:https://www.cnblogs.com/CrazyDave/p/8465383.html
Copyright © 2011-2022 走看看