码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-10-16 10:15:00      阅读:193      评论:0      收藏:0      [点我收藏+]

标签:set   一个   游戏   数组   eps   之间   pac   size   导致   

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

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=105;
const double eps=1e-6,inf=1e9;
int n,m,h[N*N],cnt,s,t,id[N][N],tot,le[N*N];
double a[N][N],b[N][N],c[N][N],sm;
struct qwe
{
    int ne,to;
    double va;
}e[N*N*N];
void add(int u,int v,double w)
{
    cnt++;
    e[cnt].ne=h[u];
    e[cnt].to=v;
    e[cnt].va=w;
    h[u]=cnt;
}
void ins(int u,int v,double w)
{
    add(u,v,w);
    add(v,u,0);
}
bool bfs()
{
    queue<int>q;
    memset(le,0,sizeof(le));
    le[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=h[u];i;i=e[i].ne)
            if(e[i].va>eps&&!le[e[i].to])
            {
                le[e[i].to]=le[u]+1;
                q.push(e[i].to);
            }
    }
    return le[t];
}
double dfs(int u,double f)
{
    if(u==t||!f)
        return f;
    double us=0;
    for(int i=h[u];i&&us<f;i=e[i].ne)
        if(e[i].va>eps&&le[e[i].to]==le[u]+1)
        {
            double t=dfs(e[i].to,min(e[i].va,f-us));
            e[i].va-=t;
            e[i^1].va+=t;
            us+=t;
        }
    if(us<eps)
        le[u]=0;
    return us;
}
int dinic()
{
    double re=0;
    while(bfs())
        re+=dfs(s,inf);
    return re;
}
bool ok(double w)
{
    memset(h,0,sizeof(h));
    cnt=1,s=0,t=n*m+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            ins(s,id[i][j],a[i][j]);
    for(int j=1;j<=m;j++)
        ins(id[1][j],t,w*b[0][j]),ins(id[n][j],t,w*b[n][j]);
    for(int i=1;i<=n;i++)
        ins(id[i][1],t,w*c[i][0]),ins(id[i][m],t,w*c[i][m]);
    for(int i=1;i<n;i++)
        for(int j=1;j<=m;j++)
            add(id[i][j],id[i+1][j],w*b[i][j]),add(id[i+1][j],id[i][j],w*b[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<m;j++)
            add(id[i][j],id[i][j+1],w*c[i][j]),add(id[i][j+1],id[i][j],w*c[i][j]);
    return sm-dinic()>eps;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%lf",&a[i][j]),id[i][j]=++tot,sm+=a[i][j];
    for(int i=0;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%lf",&b[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            scanf("%lf",&c[i][j]);
    double l=0,r=n*m*100,ans=0;
    while(r-l>1e-5)
    {
        double mid=(l+r)/2;
        if(ok(mid))
            l=mid,ans=mid;
        else
            r=mid;
    }
    printf("%.3f\n",ans);
    return 0;
}

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

标签:set   一个   游戏   数组   eps   之间   pac   size   导致   

原文地址:https://www.cnblogs.com/lokiii/p/9795805.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!