zoukankan      html  css  js  c++  java
  • UVA 1515 Pool construction 水塘(最大流,经典)

    题意:

      给一个h*w的矩阵,每个格子中是'#'和'.'两个符号之一,分别代表草和洞。现在要将洞给围起来(将草和洞分离),每条边需花费b元(即将一个洞包起来需要4边,将2个连续的洞包起来需要6边,省了2条边)。有个特殊能力,能将洞变成草,花费f。当然也能将草变成洞,花费d。围起洞来需要多少花费。矩阵四周最边边的格子都必须是草,即出现洞就必须转草。

       

      转换:洞--->草--->洞

              f     d     (元/格)

      围洞: b元/边。

    思路:

      如果能够尽量让洞都靠在一起,那肯定是最小花费,因为少了很多条边。但是转换并不是免费,如果太贵,还不如不转,直接围。

      这很像用一条绳子将洞给围起来,然后量绳子的周长。如果将格子看成点,那么就很像一个'割'(即将点分成两个集合)。问题是如何建模?

      可以这么想,假设草上面都是水,自然就会流进洞里面,那么相邻的如果是草跟洞,则这条边就会有流通过,容量最多是b(相当于绳子的一部分)。这样子每个相邻的格子都需要建2条边互指(当然需要反向边),容量都是b,使得水可以流向洞中,花费就是流量。如果此时跑一次最大流(得加源汇点),就相当于求出不作'草洞'转换的围洞花费了。

      但是问题是可以转来转去,如果草转洞很便宜,则应多转成洞。这样就需要协调了。水往低处流。我们在源点S到所有边边的草格子建立1条边,容量INF,接着,S连到所有其他的草格子,容量为d。其他的洞格子连到汇点T,容量为f。还有,每个格子和相邻的格子都有2条边,容量是b。新图就建完了,但是为什么这么做?

      如下图,9个格子,4个草格子(边边被去掉):

      . . .

      .#.

      ###

      明显,d比较小时,红色那块应该转洞,3段变1段花费。多小合适?d+b<3b时就合适,因为转的费用d,加上流进该新洞的流b,比原来需要3b要少。我们所设的(S-红草)这条边的容量是d,当满流时,也会从'红草'流向3个方向的洞中去,由红草流出的最大流不超过3b。此时,红草下的黑草,设为黑草A,它最多有b的流量可以流到红草喔,但是没办法,红草能承担的上限也只能是3b。若(S-红草)的流量>0,相当于帮忙减小了红草的压力,红草此时只是承受了来自S的d流量,还有黑草A的流量b而已。这样流来流去,红草流出的上限为3b,但是如果S给红草的d太小,即使加上黑草A的b,也是到达不了上限3b的,那么自动就被限流了。这个模型自动就被限流了,好像自动帮我们选择了“最省钱的方式”。同理,(洞-T) 也是这样,流到草格子B的流量欲超过f的话,还不如直接转成草,限流f就相当于自动转成草,如果少于f则相当于不转。

      1 #include <bits/stdc++.h>
      2 #define LL long long
      3 #define pii pair<int,int>
      4 #define INF 0x7f7f7f7f
      5 using namespace std;
      6 const int N=10000;
      7 char g[100][100];
      8 vector<int> vect[N];
      9 struct node
     10 {
     11     int from, to, cap, flow;
     12     node(){};
     13     node(int a,int b,int c,int d):from(a), to(b), cap(c), flow(d){};
     14 }edge[N*4];
     15 
     16 int edge_cnt;
     17 void add_node(int a,int b,int c,int d)
     18 {
     19     edge[edge_cnt]=node(a,b,c,d);
     20     vect[a].push_back(edge_cnt++);
     21 }
     22 
     23 bool isOk(int x,int y,int h,int w){return x>0&&x<=h&&y>0&&y<=w? 1: 0;}//判断是否在矩阵内
     24 
     25 void build_graph(int h, int w,int b, int d,int f)//建图。
     26 {
     27     for(int i=1; i<=h; i++)
     28     {
     29         for(int j=1; j<=w; j++)
     30         {
     31             if( isOk(i, j-1, h, w) )    //左边
     32             {
     33                 add_node((i-1)*w+j-1, (i-1)*w+j, b, 0);
     34                 add_node((i-1)*w+j, (i-1)*w+j-1, 0, 0);
     35             }
     36             if( isOk(i, j+1 ,h, w) )  //右边
     37             {
     38                 add_node((i-1)*w+j+1, (i-1)*w+j, b, 0);
     39                 add_node((i-1)*w+j, (i-1)*w+j+1, 0, 0);
     40             }
     41             if( isOk(i-1, j, h, w) )  //上边
     42             {
     43                 add_node((i-1)*w+j, (i-2)*w+j, b, 0);
     44                 add_node((i-2)*w+j, (i-1)*w+j, 0, 0);
     45             }
     46             if( isOk(i+1,j, h, w)  )  //下边
     47             {
     48                 add_node((i-1)*w+j, i*w+j, b, 0);
     49                 add_node(i*w+j, (i-1)*w+j, 0, 0);
     50             }
     51 
     52         }
     53     }
     54 
     55     for(int i=1; i<=h; i++)
     56     {
     57         for(int j=1; j<=w; j++)
     58         {
     59             if(i==1||i==h || j==1||j==w)  //边边的草
     60             {
     61                 add_node(0, (i-1)*w+j, INF, 0);
     62                 add_node((i-1)*w+j, 0, 0, 0);
     63             }
     64             else if(g[i][j]=='#')   //源点-草
     65             {
     66                 add_node(0, (i-1)*w+j, d, 0);
     67                 add_node((i-1)*w+j, 0, 0, 0);
     68             }
     69             else    //洞-汇点
     70             {
     71                 add_node((i-1)*w+j, w*h+1, f, 0);
     72                 add_node(w*h+1, (i-1)*w+j, 0, 0);
     73             }
     74         }
     75     }
     76 }
     77 
     78 int flow[N], path[N];
     79 
     80 int BFS(int s,int e)
     81 {
     82     deque<int> que(1,s);
     83     flow[s]=INF;
     84     while(!que.empty())
     85     {
     86         int x=que.front();
     87         que.pop_front();
     88         for(int i=0; i<vect[x].size(); i++)
     89         {
     90             node e=edge[vect[x][i]];
     91             if(!flow[e.to]&&e.cap>e.flow)
     92             {
     93                 path[e.to]=vect[x][i];
     94                 flow[e.to]=min(flow[e.from], e.cap-e.flow);
     95                 que.push_back(e.to);
     96             }
     97         }
     98         if(flow[e]) break;;
     99     }
    100     return flow[e];
    101 }
    102 
    103 
    104 
    105 int max_flow(int s,int e)
    106 {
    107     int ans_flow=0;
    108     while(true)
    109     {
    110         memset(flow,0,sizeof(flow));
    111         memset(path,0,sizeof(path));
    112 
    113         int tmp=BFS(s, e);
    114         if(tmp==0)  return ans_flow;
    115         ans_flow+=tmp;
    116 
    117         int ed=e;
    118         while(ed!=s)
    119         {
    120             int t=path[ed];
    121             edge[t].flow+=tmp;
    122             edge[t^1].flow-=tmp;
    123             ed=edge[t].from;
    124         }
    125     }
    126 }
    127 
    128 int main()
    129 {
    130     freopen("input.txt", "r", stdin);
    131     int t, w, h, d, f, b;
    132     char c;
    133     cin>>t;
    134     while(t--)
    135     {
    136         scanf("%d%d %d%d%d", &w, &h, &d, &f,&b);
    137         edge_cnt=0;
    138         memset(edge,0,sizeof(edge));
    139         for(int i=N-1; i>=0; i--)   vect[i].clear();
    140 
    141         for(int i=1; i<=h; i++)
    142         {
    143             for(int j=1; j<=w; j++)
    144             {
    145                 c=getchar();
    146                 if(c=='#'||c=='.')    g[i][j]=c;
    147                 else    j--;
    148             }
    149         }
    150         int ans=0;
    151         //处理掉四周:洞转草花费f元/格
    152         for(int i=1; i<=w; i++) if(g[1][i]!='#')    ans+=f; //第一行
    153         for(int i=1; i<=w; i++) if(g[h][i]!='#')    ans+=f; //最后一行
    154         for(int i=2; i<h; i++) if(g[i][1]!='#')    ans+=f;
    155         for(int i=2; i<h; i++) if(g[i][w]!='#')    ans+=f;
    156         build_graph(h, w, b, d, f);
    157         printf("%d
    ",max_flow(0, w*h+1)+ans);
    158     }
    159     return 0;
    160 }
    AC代码
  • 相关阅读:
    树链剖分
    后缀自动机
    莫队算法。
    线性递推BM模板
    笛卡尔积
    2019牛客暑期多校训练营(第三场) J LRU management 模拟链表操作
    线性基
    bitset 位运算
    Lindström–Gessel–Viennot lemma定理 行列式板子
    三角形
  • 原文地址:https://www.cnblogs.com/xcw0754/p/4656888.html
Copyright © 2011-2022 走看看