【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;
}
时间: 2024-10-10 14:49:08

【BZOJ3232】圈地游戏 分数规划+最小割的相关文章

bzoj 3232: 圈地游戏【分数规划+最小割】

数组开小导致TTTTTLE-- 是分数规划,设sm为所有格子价值和,二分出mid之后,用最小割来判断,也就是判断sm-dinic()>=0 这个最小割比较像最大权闭合子图,建图是s像所有点连流量为格子价值的边(相当于最大权闭合子图中的正权点),然后考虑边缘,两个相邻的格子,如果一个选一个不选那么中间这条边就有负的贡献,所以两个相邻的格子之间连两条边权为mid*边权的边,注意是两条,要互相连一下,然后所有边界上的点像t连边权为mid*边界边权的边,相当于假装外面还有一层点全标为t,然后跑最小割判断

BZOJ 3232: 圈地游戏 分数规划+判负环

3232: 圈地游戏 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 966  Solved: 466[Submit][Status][Discuss] Description DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用. DZY喜欢在地里散步.他总是从任意一个格点出发,沿着格线行走直到回到出发点,且在行走途中不允许与已走过的路线有任何相交或触碰(出发点除外).记这条封闭路线内部的格

zoj2676--Network Wars(0-1分数规划+最小割)

zoj2676:题目链接 题目大意:有一个n个点的网络,其中有m条光缆(所有的点都被连接,任意两个点之间最多有一条,不存在连接自身的),每条光缆有一定的价值,网络中1为起点,n为终点,现在要求找出一些光缆能分割开1到n,使它们不能相互通信,并且要求花费的和除以光缆数的值最小.输出选择的光缆的编号. 从问题中可以看出一定是0-1分数规划的题目,假设选出光缆的集合M,M为原图的一个割,光缆si∈M,价值为ci,数量k = 1 ,可以推出g(x) = min( ∑c - x*∑k ),因为si是割中的

【POJ3155】Hard Life 分数规划+最小割

链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/46437961"); } 题解: 如题.先算出那个分数值,然后看有哪些人还与源点相连. 最小割建图:原图每个点对应一个点,原图每条边对应一个点.每条边对应点向两端点对应点连边,注意要单向边. 这道题卡精度: 所以一些细

HDU 2676 Network Wars 01分数规划,最小割 难度:4

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1676 对顶点i,j,起点s=1,终点t=n,可以认为题意要求一组01矩阵use[i][j],使得aveCost=sigma(use[i][j]*cost[i][j])/sigma(use[i][j])最小,且{(i,j)|use[i][j]==1}是图的S-T割 定义F(e)=min(sigma(use[i][j]*(cost[i][j]-a))),明显,F(e)是目标式的变

BZOJ3232: 圈地游戏

题解: 神题一道... 题解戳这里:http://hi.baidu.com/strongoier/item/0425f0e5814e010265db0095 分数规划可以看这里:http://blog.csdn.net/hhaile/article/details/8883652 无限orzzzzz 代码:实数网络流真蛋疼... 1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #inclu

[TJOI2009]战争游戏 - 网络流,最小割

Description 小R正在玩一个战争游戏.游戏地图是一个M行N列的矩阵,每个格子可能是障碍物,也可能是空地,在游戏开始时有若干支敌军分散在不同的空地格子中.每支敌军都可以从当前所在的格子移动到四个相邻的格子之一,但是不能移动到包含障碍物的格子.如果敌军移动出了地图的边界,那么战争就失败了. 现在你的任务是,在敌军开始移动前,通过飞机轰炸使得某些原本是空地的格子变得不可通行,这样就有可能阻止敌军移出地图边界(出于某种特殊的考虑,你不能直接轰炸敌军所在的格子).由于地形不同的原因,把每个空地格

分数规划小结

https://www.zybuluo.com/ysner/note/1262173 定义 给定数列\(\{a\},\{b\}\),求解一组数列\(\{x\}\)(\(x_i=\{0,1\}\)) 使得\[\frac{\sum_{i=1}^na_i*x_i}{\sum_{i=1}^nb_i*x_i}\] 最大化. 方法 主要是二分答案. 设二分出的值为\(mid\), 则应有\[\frac{\sum_{i=1}^na_i*x_i}{\sum_{i=1}^nb_i*x_i}\geq mid\] 化

zoj 2676 Network Wars(最小割,01分数规划)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2676 大致题意:给出一个带权无向图,每条边有一个边权wi,求将S和T分开的一个割边集C,使得该割边集的平均边权最小,即最小化∑wi / |C| . 详见amber关于最小割模型的论文 思路:amber论文中详细讲解了如何转化成函数及建图,值得注意的是当边被重新赋权后,对于wi < 0 的边权,该边必然在最小割中,不必再建边,直接加入最大流中即可,因为求最小割时边权都为正值