• 【BZOJ3232】圈地游戏


    题面

    http://darkbzoj.tk/problem/3232

    题解

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

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

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

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

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

    拿$sum-flow$就是$V-mid imes 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;
    }
  • 相关阅读:
    *args和**kwargs
    事件驱动模型
    同步异步和阻塞非阻塞
    多进程和多线程
    认识tornado(五)
    认识tornado(四)
    认识tornado(三)
    [GO]使用select实现超时
    [GO]使用select实现斐波那契
    [GO]ticker的使用
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11287193.html
Copyright © 2020-2023  润新知