[BZOJ3232]圈地游戏:0/1分数规划+最小割

分析:

二分答案后跑最小割。
连边方式如下:
1、s -> 格子,容量为格子的权值。
2、格子 -> 相邻格,容量为两格之间的格线权值mid。
3、边界格 -> t,容量为该格的边界格线权值
mid。
剩下的待会补。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>

int n,m,s,t,ecnt,head[2505],cur[2505],dep[2505];
int val[55][55],D[55][55],R[55][55];
int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};
double sum,maxflow,mincut;
std::queue<int> q;
struct Edge{
    int to,nxt;
    double cap;
}e[30005];

inline void add_edge(int bg,int ed,double ca){
    ecnt++;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    e[ecnt].cap=ca;
    head[bg]=ecnt;
}

inline bool bfs(){
    memset(dep,0x3f,sizeof dep);
    for(int i=1;i<=t;i++) cur[i]=head[i];
    while(!q.empty()) q.pop();
    dep[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int ver=e[i].to;
            if(dep[ver]>1e9&&e[i].cap>0){
                dep[ver]=dep[u]+1;
                q.push(ver);
            }
        }
    }
    return dep[t]<1e9;
}

double dfs(int x,double pref){
    if(!pref||x==t) return pref;
    double flow=0,temp;
    for(int& i=cur[x];i!=-1;i=e[i].nxt){
        int ver=e[i].to;
        if(dep[ver]==dep[x]+1&&(temp=dfs(ver,std::min(pref,e[i].cap)))){
            e[i].cap-=temp;
            e[i^1].cap+=temp;
            pref-=temp;
            flow+=temp;
            if(!pref) break;
        }
    }
    return flow;
}

inline void dinic(){
    maxflow=0;
    while(bfs()){
        maxflow+=dfs(s,1e9);
//      std::cout<<maxflow<<std::endl;
    }
}

inline bool check(double mid){
    ecnt=-1;memset(head,-1,sizeof head);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            add_edge(s,(i-1)*m+j,val[i][j]);
            add_edge((i-1)*m+j,s,0);
            for(int k=1;k<=4;k++){
                int xx=i+dx[k],yy=j+dy[k];
                if(xx==0){
                    add_edge((i-1)*m+j,t,D[i-1][j]*mid);
                    add_edge(t,(i-1)*m+j,0);
                }
                else if(xx==n+1){
                    add_edge((i-1)*m+j,t,D[i][j]*mid);
                    add_edge(t,(i-1)*m+j,0);
                }
                else if(yy==0){
                    add_edge((i-1)*m+j,t,R[i][j-1]*mid);
                    add_edge(t,(i-1)*m+j,0);
                }
                else if(yy==m+1){
                    add_edge((i-1)*m+j,t,R[i][j]*mid);
                    add_edge(t,(i-1)*m+j,0);
                }
                else{
                    add_edge((i-1)*m+j,(xx-1)*m+yy,(k<=2?(k==1?D[i-1][j]:D[i][j]):(k==3?R[i][j-1]:R[i][j]))*mid);
                    add_edge((xx-1)*m+yy,(i-1)*m+j,0);
                }
            }
        }
    }
//  for(int i=1;i<=t;i++){
//      std::cout<<i<<": ";
//      for(int j=head[i];j!=-1;j=e[j].nxt)
//          std::cout<<"("<<e[j].to<<","<<e[j].cap<<") ";
//      std::cout<<std::endl;
//  }
//  system("pause");
    dinic();
    mincut=maxflow;
    return sum-mincut>0;
}

int main(){
    scanf("%d%d",&n,&m);
    s=n*m+1;t=s+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&val[i][j]),sum+=val[i][j];
    for(int i=0;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&D[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            scanf("%d",&R[i][j]);
    double l=0,r=1250,ans;
    while(r-l>1e-6){
//      std::cout<<">>>"<<l<<" "<<r<<std::endl;
        double mid=(l+r)/2;
        if(check(mid)) ans=mid,l=mid;
        else r=mid;
    }
    printf("%.3lf\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9691926.html