bzoj 3232 圈地游戏——0/1分数规划(或网络流)

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3232

当然是0/1分数规划。但加的东西和减的东西不在一起,怎么办?

考虑把它们合在一起。因为边围成的形状像一个环,所以把格子的贡献也放到边上,然后正常判环。

放到边上的方法就是:比如竖着的边,可以在每一行上维护该行格子值前缀和,然后指定那个围成的形状是,比如,逆时针的,那么向上的边就加上到它为止的前缀值,向下的边就减去到它为止的前缀值,然后就能判环了!

这样一定只有一个环。但多个环答案不会更优。

还可以用网络流。与 s 相连表示选、与 t 相连表示不选的话,每个点到 s 连该点权值的边,到 t 连边权为0的边,相邻点之间连它们夹着的边权值的边,这样如果相邻的点一个选了一个没选,就得割它们之间的那条边,就能表示了。

自己写了判环的那个。

注意如果以竖着的边算了围住的部分,就不要再用横着的边同时算了!!

请把 eps 设成 1e-7 而不是 1e-5 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define db double
using namespace std;
const int N=55,M=N*N;
const db eps=1e-7;
int n,m,fl[N][N]/*,fu[N][N]*/,eh[N][N],el[N][N],cnt[N][N],tot;
db l,r,mid,ans,dis[N][N],w[N][N][5];
bool vis[N][N];
queue<pair<int,int> > q;
bool spfa()
{
//    printf("mid=%.3lf\n",mid);
    while(q.size())q.pop();
    for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            q.push(make_pair(i,j));
            vis[i][j]=1; dis[i][j]=0; cnt[i][j]=0;
            if(i)
            {
                w[i][j][0]=fl[i][j]-mid*el[i][j];
//                if(mid<3&&mid>2&&el[i][j]==1)
//                printf("w[%d][%d][0]=%.3lf\n",i,j,w[i][j][0]);
            }
            if(j)
            {
                w[i][j][1]=/*-fu[i][j]*/-mid*eh[i][j];
//                if(mid<3&&mid>2&&eh[i][j]==1)
//                printf("w[%d][%d][1]=%.3lf\n",i,j,w[i][j][1]);
            }
            if(i<n)
            {
                w[i][j][3]=-fl[i+1][j]-mid*el[i+1][j];
//                if(mid<3&&mid>2&&el[i+1][j]==1)
//                printf("w[%d][%d][3]=%.3lf\n",i,j,w[i][j][3]);
            }
            if(j<m)
            {
                w[i][j][2]=/*fu[i][j+1]*/-mid*eh[i][j+1];
//                if(mid<3&&mid>2&&eh[i][j+1]==1)
//                printf("w[%d][%d][2]=%.3lf\n",i,j,w[i][j][2]);
            }
        }
    while(q.size())
    {
        int x=q.front().first,y=q.front().second;
        q.pop();
        vis[x][y]=0;
//        if(mid>2&&mid<3)printf("x=%d y=%d cnt=%d dis=%.3lf\n",x,y,cnt[x][y],dis[x][y]);
//        if(mid>2&&mid<3)printf("fa[%d][%d]=(%d,%d)\n",x,y,fa[x][y][0],fa[x][y][1]);
        if(x&&dis[x-1][y]<dis[x][y]+w[x][y][0])
        {
            dis[x-1][y]=dis[x][y]+w[x][y][0];
//            printf("  w[%d][%d][0]=%.3lf\n",x,y,w[x][y][0]);
//            fa[x-1][y][0]=x; fa[x-1][y][1]=y;
            cnt[x-1][y]=cnt[x][y]+1;
            if(cnt[x-1][y]==tot)
            {
//                if(mid>2&&mid<3)
//                    printf("x-1=%d y=%d dis=%.3lf\n",x-1,y,dis[x-1][y]);
                return 1;
            }
            if(!vis[x-1][y])
                vis[x-1][y]=1,q.push(make_pair(x-1,y));
        }
        if(y&&dis[x][y-1]<dis[x][y]+w[x][y][1])
        {
            dis[x][y-1]=dis[x][y]+w[x][y][1];
//            printf("  w[%d][%d][1]=%.3lf\n",x,y,w[x][y][1]);
//            fa[x][y-1][0]=x; fa[x][y-1][1]=y;
            cnt[x][y-1]=cnt[x][y]+1;
            if(cnt[x][y-1]==tot)
            {
//                if(mid>2&&mid<3)
//                    printf("x=%d y-1=%d dis=%.3lf\n",x,y-1,dis[x][y-1]);
                return 1;
            }
            if(!vis[x][y-1])
                vis[x][y-1]=1,q.push(make_pair(x,y-1));
        }
        if(x<n&&dis[x+1][y]<dis[x][y]+w[x][y][3])
        {
            dis[x+1][y]=dis[x][y]+w[x][y][3];
//            printf("  w[%d][%d][3]=%.3lf\n",x,y,w[x][y][3]);
//            fa[x+1][y][0]=x; fa[x+1][y][1]=y;
            cnt[x+1][y]=cnt[x][y]+1;
            if(cnt[x+1][y]==tot)
            {
//                if(mid>2&&mid<3)
//                    printf("x+1=%d y=%d dis=%.3lf\n",x+1,y,dis[x+1][y]);
                return 1;
            }
            if(!vis[x+1][y])
                vis[x+1][y]=1,q.push(make_pair(x+1,y));
        }
        if(y<m&&dis[x][y+1]<dis[x][y]+w[x][y][2])
        {
            dis[x][y+1]=dis[x][y]+w[x][y][2];
//            printf("  w[%d][%d][2]=%.3lf\n",x,y,w[x][y][4]);
//            fa[x][y+1][0]=x; fa[x][y+1][1]=y;
            cnt[x][y+1]=cnt[x][y]+1;
            if(cnt[x][y+1]==tot)
            {
//                if(mid>2&&mid<3)
//                    printf("x=%d y+1=%d dis=%.3lf\n",x,y+1,dis[x][y+1]);
                return 1;
            }
            if(!vis[x][y+1])
                vis[x][y+1]=1,q.push(make_pair(x,y+1));
        }
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m); tot=(n+1)*(m+1);//+1!!!
    for(int i=1;i<=n;i++)
        for(int j=1,d;j<=m;j++)
        {
            scanf("%d",&d); r+=d;
            fl[i][j]=fl[i][j-1]+d;
//            fu[i][j]=fu[i-1][j]+d;
        }
    for(int i=0;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&eh[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            scanf("%d",&el[i][j]);
    while(r-l>eps)
    {
        mid=(l+r)/2;
        if(spfa()) ans=mid,l=mid+eps;
        else r=mid-eps;
    }
    printf("%.3lf\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/9709181.html

时间: 2024-11-05 18:52:54

bzoj 3232 圈地游戏——0/1分数规划(或网络流)的相关文章

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

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

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

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

0/1分数规划

学习了lyd书上的0/1分数规划,发现这类题目都有一个特点,就是求$\frac{\sum_{a_{i}*x_{i}}}{\sum_{b_{i}*x_{i}}}$的最大或者最小,再加一些限制取不取的条件. POJ2976 二分答案+sort取前(n-k+1)个. #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; con

POJ - 2976 Dropping tests &amp;&amp; 0/1 分数规划

POJ - 2976 Dropping tests 你有 \(n\) 次考试成绩, 定义考试平均成绩为 \[\frac{\sum_{i = 1}^{n} a_{i}}{\sum_{i = 1}^{n} b_{i}}\] 你可以考虑放弃 \(K\) 次成绩, 求最大平均成绩 * 100 小插曲: 被精度卡成喜羊羊 0/1分数规划\(from\)人生导师 Solution 01分数规划(不是很)裸题, 在每次 \(check\) 时, 选取较大的 \(num - K + 1\) 次即可 Code #

2019.4.9 一题——概率期望+0/1分数规划+最大权闭合子图

没注意 “第 x 条边和第 y 条边的起点是相同的” 的限制.没想出来. 有这个限制,可以考虑每个点分别计算.令 \( f[i] \) 表示从 i 出发的最大边数期望,那么先把拓扑序在自己之后的点的 \( f[ ] \) 算出来,然后考虑自己这个点的出边怎么做能使自己的 \( f[ ] \) 最大. \( f[i]=\frac{ \sum f[j]+1 }{ d } \) ,其中 d 是保留下来的边数, j 是保留边指向的点. 如果把 \( f[ ]+1 \) 看做收益, 1 看做代价,那么这个

『0/1分数规划 二分法』

0/1分数规划 模型 0/1分数规划指的是这样一个问题模型: 给定整数\(a_1,a_2,...,a_n\)和\(b_1,b_2,...,b_n\),求一组解\(x_1,x_2,...,x_n(\forall\ i\in[1,n],x_i=1,0)\),使得下式最大化:\[\frac{\sum_{i=1}^na_i*x_i}{\sum_{i=1}^nb_i*x_i}\] 简单地说,就是给定\(n\)对整数\(a_i,b_i\),从中选取若干对,使得选出的\(a\)之和与\(b\)之和的比值最大.

[例题/总结]0/1分数规划

[TOC] ##一.总述 0/1分数规划是专门解决0/1分数规划模型的一种算法~~(废话)~~.所以说0/1分数规划模型是什么呢?给定整数{\(a_1,a_2,a_3,...,a_n\)},{\(b_1,b_2,b_3,...,b_n\)}从中选出若干对数,使得它们各自和的比值最大.公式如下: \(\frac{\sum_{p=1}^{n}a_p\times x_p}{\sum_{p=1}^{n}b_p\times x_p}(x_p=1,0)\) ##二.实现原理 那么我们用什么方法可以求出这样一

Yougth的最大化(好题,二分查找 0 1分数规划)

Yougth的最大化 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 Yougth现在有n个物品的重量和价值分别是Wi和Vi,你能帮他从中选出k个物品使得单位重量的价值最大吗? 输入 有多组测试数据每组测试数据第一行有两个数n和k,接下来一行有n个数Wi和Vi.(1<=k=n<=10000) (1<=Wi,Vi<=1000000) 输出 输出使得单位价值的最大值.(保留两位小数) 样例输入 3 2 2 2 5 3 2 1 样例输出 0.75 1 #in

bzoj 1690: [Usaco2007 Dec]奶牛的旅行——分数规划+spfa判负环

Description 作为对奶牛们辛勤工作的回报,Farmer John决定带她们去附近的大城市玩一天.旅行的前夜,奶牛们在兴奋地讨论如何最好地享受这难得的闲暇. 很幸运地,奶牛们找到了一张详细的城市地图,上面标注了城市中所有L(2 <= L <= 1000)座标志性建筑物(建筑物按1..L顺次编号),以及连接这些建筑物的P(2 <= P <= 5000)条道路. 按照计划,那天早上Farmer John会开车将奶牛们送到某个她们指定的建筑物旁边,等奶牛们完成她们的整个旅行并回到