zoukankan      html  css  js  c++  java
  • 【BZOJ3232】圈地游戏 分数规划+最小割

    【BZOJ3232】圈地游戏

    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

    HINT

    题解:感觉分数规划的题经常和网络流搭配,但建图还是我的弱项啊~

    先二分答案mid,将所有边的权值变成(mid*边权),然后相当于跑一个点权为正,边权为负的最大权闭合图,然后思考怎么建图

    1.从S向所有点连一条容量为点权的边,这是由最大权闭合图的思想得到
    2.从所有点向相邻的点连一条容量为(mid*边权)的边(具体地说,是两条有向边),这个在两个点都选或都不选的时候没什么用,但是如果一个选另一个不选,那么就相当于要付出这条边边权的代价
    3.从所有边界上的点向T连一条容量为(mid*边权)的边,这个可以理解为在网格外面还有一圈的点,这些点必须不选,也就相当于这些点可以和T看成一个点。那么如果选了边界上的点,必须付出这些边界上的边权 的代价。

    这样最优代价和就变成了(所有正权和-最小割),如果是正值,就调整l,否则调整r

    如果你不理解这样建图的可行性,可以yy一下:

    如果我们选出了这样一些点,那么他们和相邻的不选的点(包括网格外的点)都要用边隔开,也就是说我们要付出这些边的代价,(2)(3)中连的边可以满足这个要求;此时,其余的点都不能选,意味着他们都要和S隔开,意味着我们要付出这些点点权的代价,(1)中连的边可以满足这些要求;又因为所有不选的点一定会间接的和网格外的点相连,那么它们自然地被划分到了T集合中,当然,选的点也已经被划分到了S集合中,此时我们已经成功的将所有代价都 割 掉了

    如果还感觉说的不清楚的话可能是我自己理解的也不够 细 吧~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #define eps 1e-6
    #define P(A,B) ((A-1)*m+B)
    using namespace std;
    int n,m,cnt,S,T;
    int d[3000],to[500000],next[500000],head[3000];
    int map[60][60],e1[60][60],e2[60][60];
    double val[500000],ans,tot;
    queue<int> q;
    double dfs(int x,double mf)
    {
        if(x==T)    return mf;
        int i;
        double temp=mf,k;
        for(i=head[x];i!=-1;i=next[i])
        {
            if(val[i]>eps&&d[to[i]]==d[x]+1)
            {
                k=dfs(to[i],min(temp,val[i]));
                if(k<eps)    d[to[i]]=0;
                val[i]-=k,val[i^1]+=k,temp-=k;
                if(temp<eps) break;
            }
        }
        return mf-temp;
    }
    int bfs()
    {
        while(!q.empty())   q.pop();
        memset(d,0,sizeof(d));
        int i,u;
        q.push(S),d[S]=1;
        while(!q.empty())
        {
            u=q.front(),q.pop();
            for(i=head[u];i!=-1;i=next[i])
            {
                if(!d[to[i]]&&val[i]>eps)
                {
                    d[to[i]]=d[u]+1;
                    if(to[i]==T)    return 1;
                    q.push(to[i]);
                }
            }
        }
        return 0;
    }
    void add(int a,int b,double c)
    {
        to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
        to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
    }
    bool check(double sta)
    {
        int i,j;
        memset(head,-1,sizeof(head));
        cnt=0;
        S=0,T=n*m+1;
        ans=0.0;
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                add(0,P(i,j),1.0*map[i][j]);
        for(i=1;i<n;i++)
            for(j=1;j<=m;j++)
                add(P(i,j),P(i+1,j),sta*e1[i][j]),add(P(i+1,j),P(i,j),sta*e1[i][j]);
        for(i=1;i<=n;i++)
            for(j=1;j<m;j++)
                add(P(i,j),P(i,j+1),sta*e2[i][j]),add(P(i,j+1),P(i,j),sta*e2[i][j]);
        for(i=1;i<=m;i++)    add(P(1,i),T,sta*e1[0][i]),add(P(n,i),T,sta*e1[n][i]);
        for(i=1;i<=n;i++)    add(P(i,1),T,sta*e2[i][0]),add(P(i,m),T,sta*e2[i][m]);
        while(bfs())    ans+=dfs(0,99999999.999999);
        return tot-ans>eps;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int i,j;
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                scanf("%d",&map[i][j]),tot+=1.0*map[i][j];
        for(i=0;i<=n;i++)
            for(j=1;j<=m;j++)
                scanf("%d",&e1[i][j]);
        for(i=1;i<=n;i++)
            for(j=0;j<=m;j++)
                scanf("%d",&e2[i][j]);
        double l=0.0,r=n*m*100.0,mid;
        while(r-l>eps)
        {
            mid=(l+r)*0.5;
            if(check(mid))  l=mid;
            else    r=mid;
        }
        printf("%.3f",l);
        return 0;
    }
  • 相关阅读:
    .NET Cache缓存
    异步
    es6常用功能
    vue-router路由懒加载
    vue中nextTick和$nextTick
    动态模板中 swiper 划不动问题
    javaScript正则判断手机号
    Mac终端使用技巧
    alert IOS自带域名
    vue css background路径不对
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6790404.html
Copyright © 2011-2022 走看看