【BZOJ3232】圈地游戏

题面

http://darkbzoj.tk/problem/3232

题解

非常神仙的一道题。(跟$yyb$的评价一样)

这道题其实不是最大权闭合子图。是一个普通的最小割。

首先,分数规划,二分答案。

其次,先把所有格子的权值都加上,

让$S$流向每个格子,边权为$V[i][j]$,然后我们在各格子间连边,权值为格线的权。

拿$sum-flow$就是$V-mid \times c$的最大值。

因为这样可以表达这样的关系:想留下每个格子,就要割掉它周围的边(或它周围的格子),若割掉的联通块中有格子被割了,不如不割。

但是这样没有汇点,求最小割是没有意义的。

考虑限制,取到外面一定不合法。

让$T$代表外面,让每个边缘的格子向$T$连边,边权为对应格线的权。求最小割就行了。

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 5050
#define INF 1000000007
#define ri register int
#define db double
#define eps 1e-5
using namespace std;

int n,m;
int a[100][100],b[100][100],c[100][100];

struct graph {
  #define S 0
  #define T (n*m+1)
  vector<db> w; vector<int> to,ed[N];
  int d[N],cur[N];
  void add_edge(int a,int b,db tw) {
    w.push_back(tw); to.push_back(b); ed[a].push_back(to.size()-1);
    w.push_back(0);  to.push_back(a); ed[b].push_back(to.size()-1);
  }
  void add_edges(int a,int b,db tw) {
    w.push_back(tw); to.push_back(b); ed[a].push_back(to.size()-1);
    w.push_back(tw); to.push_back(a); ed[b].push_back(to.size()-1);
  }
  void clear() {
    for (ri i=S;i<=T;i++) ed[i].clear();
    to.clear(); w.clear();
  }
  bool bfs() {
    memset(d,0x3f,sizeof(d));
    queue<int> q; d[S]=0; q.push(0);
    while (!q.empty()) {
      int x=q.front(); q.pop();
      for (ri i=0,l=ed[x].size();i<l;i++) {
        int e=ed[x][i];
        if (d[to[e]]>d[x]+1 && w[e]-eps>0) {
          d[to[e]]=d[x]+1;
          q.push(to[e]);
        }
      }
    }
    return d[T]<INF;
  }
  db dfs(int x,db limit) {
    if (x==T) return limit;
    db tot=0;
    for (ri &i=cur[x];i<ed[x].size();i++) {
      int e=ed[x][i];
      if (d[x]+1==d[to[e]] && w[e]-eps>0) {
        db f=dfs(to[e],min(limit,w[e]));
        if (f-eps<=0) continue;
        w[e]-=f; w[1^e]+=f;
        tot+=f; limit-=f;
        if (limit-eps<=0) return tot;
      }
    }
    return tot;
  }
  db dinic() {
    db ret=0;
    while (bfs()) {
      memset(cur,0,sizeof(cur));
      ret+=dfs(S,INF);
    }
    return ret;
  }
} G;

bool check(db mid) {
  G.clear();
  db sum=0;
  for (ri i=1;i<=n;i++) {
    for (ri j=1;j<=m;j++) {
      G.add_edge(S,(i-1)*m+j,a[i][j]);
      sum+=a[i][j];
    }
  }
  for (ri i=1;i<=n+1;i++) {
    for (ri j=1;j<=m;j++) {
      if (i==1) G.add_edge(j,T,b[i][j]*mid);
      else if (i==n+1) G.add_edge((n-1)*m+j,T,b[i][j]*mid);
      else G.add_edges((i-2)*m+j,(i-1)*m+j,b[i][j]*mid);
    }
  }
  for (ri i=1;i<=n;i++) {
    for (ri j=1;j<=m+1;j++) {
      if (j==1) G.add_edge((i-1)*m+1,T,c[i][j]*mid);
      else if (j==m+1) G.add_edge((i-1)*m+m,T,c[i][j]*mid);
      else G.add_edges((i-1)*m+j-1,(i-1)*m+j,c[i][j]*mid);
    }
  }
  if (sum-G.dinic()>0) return 1; else return 0;
}

int main() {
  scanf("%d %d",&n,&m);
  for (ri i=1;i<=n;i++)
    for (ri j=1;j<=m;j++) scanf("%d",&a[i][j]);
  for (ri i=1;i<=n+1;i++)
    for (ri j=1;j<=m;j++) scanf("%d",&b[i][j]);
  for (ri i=1;i<=n;i++)
    for (ri j=1;j<=m+1;j++) scanf("%d",&c[i][j]);
  db lb=0,rb=407.0,ans;
  while (rb-lb>1e-5) {
    db mid=(lb+rb)/2;
    if (check(mid)) ans=lb=mid; else rb=mid;
  }
  printf("%.3lf",ans);
  return 0;
}

 

posted @ 2019-08-02 10:44  HellPix  阅读(317)  评论(0编辑  收藏  举报