• [Bzoj3232]圈地游戏(分数规划+最小割/spfa判负环)


    Description

    DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用。
    DZY喜欢在地里散步。他总是从任意一个格点出发,沿着格线行走直到回到出发点,且在行走途中不允许与已走过的路线有任何相交或触碰(出发点除外)。记这条封闭路线内部的格子总价值为V,路线上的费用总和为C,DZY想知道V/C的最大值是多少。

    Input

    第一行为两个正整数n,m。
    接下来n行,每行m个非负整数,表示对应格子的价值。
    接下来n+1行,每行m个正整数,表示所有横向的格线上的费用。
    接下来n行,每行m+1个正整数,表示所有纵向的格线上的费用。
    (所有数据均按从左到右,从上到下的顺序输入,参见样例和配图)

    Output

     
    输出一行仅含一个数,表示最大的V/C,保留3位小数。

    Sample Input

    3 4
    1 3 3 3
    1 3 1 1
    3 3 1 0
    100 1 1 1
    97 96 1 1
    1 93 92 92
    1 1 90 90
    98 1 99 99 1
    95 1 1 1 94
    1 91 1 1 89

    Sample Output

    1.286
     
    这道题调了很久,为了复习dinic特意在写这题前先去写一遍格子取数(然后调了一晚上)。
    不难发现这道题具有分数规划的性质。我们要求格子的和处以边的和。具体的分数规划可以参考我的另一篇博客 链接
    二分答案mid值,
     
    然后如何判断 sum_d(被圈进去的格子总权) - mid×c(边权×预计答案,不理解请重新学习分数规划)<0 就有两种做法。
    一种是差分,然后以边为结点建图,沿横边正向走为加,反向走为减,最后就是判图中是否有负环。这里不做解释。
    我用的是最小割的解法。
    我们考虑如果以界外为汇点,界内所有格子连向源点,格子间都连上边,边的流量就为mid*c 。那么最小割就能满足我们选择了单独的一个封闭区间,而最小割的值就是mid×c+lose_d(不被选用的点权和),我们用点权总和减去最小割的值,就是sum_d-mid*c。这个值如果大于0,就把mid往大了取,如果小于0,就往小了取。
    注意精度!!!我被卡了精度wa了三次(我甚至上dbzoj下了数据,然后就新人求助,本机ac,提交wa),最后把所有可能损失精度的地方加优化。
    代码:
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define maxn 55
    #define INF 99999999.999999
    #define eps 1e-6
    using namespace std;
    inline void read(int &x){
        x=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    int N,M;
    double ans,sum;
    int p(int a,int b){
        return (a-1)*M+b;
    }
    int map[maxn][maxn];
    int up[maxn][maxn],ls[maxn][maxn];
    struct node{
        int nex,to;
        double w;
    }edge[500000];
    int head[3000],tot;
    int S,T;
    inline void insert(int from,int to,double w){
        edge[tot].nex=head[from];
        head[from]=tot;
        edge[tot].to=to;
        edge[tot].w=w;
        tot++;
        edge[tot].nex=head[to];
        head[to]=tot;
        edge[tot].to=from;
        edge[tot].w=0;
        tot++;
    }
    int dept[3000];
    bool bfs(){
        memset(dept,0,sizeof(dept));
        queue<int>que;
        que.push(S);
        dept[S]=1;
        int x;
        while(!que.empty()){
            x=que.front();
            que.pop();
            for(int i=head[x];i!=-1;i=edge[i].nex)
                if(edge[i].w>eps&&!dept[edge[i].to]){
                    dept[edge[i].to]=dept[x]+1;
                    que.push(edge[i].to);
                    if(edge[i].to==T)
                        return 1;
                }
        }
        return 0;
    }
    double dfs(int x,double f){
        if(x==T)
            return f;
        double tmp=f;
        for(int i=head[x];i!=-1;i=edge[i].nex)
            if(edge[i].w>eps&&dept[edge[i].to]==dept[x]+1){
                double cal=dfs(edge[i].to,min(tmp,edge[i].w));
                if(cal<eps)
                    dept[edge[i].to]=0;
                edge[i].w-=cal;
                edge[i^1].w+=cal;
                tmp-=cal;
                if(tmp<eps)    
                    break ;
            }
        return f-tmp;
    }
    void dinic(){
        while(bfs()){
            //for(int i=1;i<=N*M;i++)
            //    cout<<dept[i]<<" ";
            //cout<<endl;
            //printf("%.3f
    ",ans);
            //system("pause");
            ans+=dfs(S,INF);
        }
    }
    bool check(double x){
        memset(head,-1,sizeof(head));
        tot=0;ans=0.0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=M;j++)
                insert(S,p(i,j),1.0*map[i][j]);
        for(int i=1;i<N;i++)
            for(int j=1;j<=M;j++){
                insert(p(i,j),p(i+1,j),x*up[i][j]);
                insert(p(i+1,j),p(i,j),x*up[i][j]);
            }
        for(int i=1;i<=N;i++)
            for(int j=1;j<M;j++){
                insert(p(i,j),p(i,j+1),x*ls[i][j]);
                insert(p(i,j+1),p(i,j),x*ls[i][j]);
            }
        for(int i=1;i<=M;i++){
            insert(p(1,i),T,x*up[0][i]);
            insert(p(N,i),T,x*up[N][i]);
        }    
        for(int i=1;i<=N;i++){
            insert(p(i,1),T,x*ls[i][0]);
            insert(p(i,M),T,x*ls[i][M]);
        }    
        dinic();
        //cout<<sum<<" ";
        //printf("%.3f
    ",ans);
        return sum-ans>eps;
    }
    int main(){
        //freopen("10.in","r",stdin);
        read(N);read(M);
        S=0;T=N*M+1;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=M;j++)    
                read(map[i][j]),sum+=1.0*map[i][j];
        for(int i=0;i<=N;i++)
            for(int j=1;j<=M;j++)    
                read(up[i][j]);
        for(int i=1;i<=N;i++)
            for(int j=0;j<=M;j++)    
                read(ls[i][j]);
        double l=0.0,r=N*M*100.0,mid;
        while(r-l>eps)
        {
            mid=(l+r)*0.5;
            //cout<<l<<" "<<r<<" "<<mid<<endl;
            if(check(mid))  
                l=mid;
            else    
                r=mid;
        }
        printf("%.3lf",l);
        return 0;
    }
    /*
    3 4
    1 3 3 3
    1 3 1 1
    3 3 1 0
    100 1 1 1
    97 96 1 1
    1 93 92 92
    1 1 90 90
    98 1 99 99 1
    95 1 1 1 94
    1 91 1 1 89
    */
    View Code
     
  • 相关阅读:
    算法 在连续线性空间里查找
    OSI网络模型和网络连接设备
    CPU 架构SMP/NUMA,调优
    cluster集群基本概念
    protocol buffers 使用方法
    Qt在window下的环境变量PATH的配置
    mysql connector c++ 1.1 API初步体验
    mysql connector c++ 1.1 安装
    python知识点总结01(不定时更新)
    ant-design表单自定义验证
  • 原文地址:https://www.cnblogs.com/sherrlock/p/9930194.html
Copyright © 2020-2023  润新知