最小割 UVA 1515 Pool construction

Description

Download as PDF

You are working for the International Company for Pool Construction, a construction company which specializes in building swimming pools. A new client wants to build several new pool areas.

A pool area is a rectangular grid of w x h square patches, consisting of zero or more (possibly disconnected) pools. A pool consists of one or multiple connected hole patches, which will later be filled with water. In the beginning, you start with a piece of land where each patch is either a hole in the ground ('.') or flat grass ('#'). In order to transform this land into a pool area, you must adhere to the following:


  • You can leave a patch as it is. This costs nothing.
  • If the patch is grass in the beginning, you can dig a hole there. This costs d EUR.
  • If the patch is a hole in the beginning, you can fill the hole and put grass on top. This costs f EUR.
  • You must place special boundary elements along each edge running between a final grass patch and a final hole patch, to ensure that water does not leak from the pool. This costs b EUR per boundary element.
  • The outermost rows and columns of the pool area must always be grass.


You are given the task of calculating the cost of the cheapest possible pool area given the layout of the existing piece of land.

Input

On the first line a positive integer: the number of test cases, at most 100. After that per test case:


  • one line with two integers w and h (2$ \le$wh$ \le$50): the width and height of the building site.
  • one line with three integers df and b (1$ \le$dfb$ \le$10000): the costs for digging a new hole, filling an existing hole, and building a boundary element between a pool and grass patch.
  • h lines of w characters each, denoting the layout of the original building site.

Output

Per test case:


  • one line with an integer: the cost of building the cheapest possible pool area from the original piece of land.

Sample Input

3
3 3
5 5 1
#.#
#.#
###
5 4
1 8 1
#..##
##.##
#.#.#
#####
2 2
27 11 11
#.
.#

Sample Output

9
27
22



思路:我们把每个格子拆成两个点,一个当作是草,一个是洞, 然后我们尝试构造一个最小割模型,因为最小割能解决一下冲突的处理问题,像这里冲突在一个点不能同时是洞和草,然后相邻如果不一样要多加一个数,然后根据这个,我们要想方设法构造出一个图,如果我们选择了“草”格子,那么不能从源点经过这个“洞”格子到达汇点,如果相邻格子不一样,我们必须要有一个流量为b的流量可能留到汇点,我们只要将其删掉就行了,恰好就是加上了一个额外的流量。然后如果构造好后,最小割就是答案,因为无法从源点到达汇点,所以没有任何不符合要求的地方出现,同时是最小的。建图方式:源点到“草”点,容量是对应的花费,“草”点到“洞”点,容量同样是对应的话费,“洞”点到汇点,容量为inf, 然后相邻节点的“草”点互相有一条边,容量为b,最后跑个网络流就行了。


代码:

#include <iostream>
#include <cstring>
#include <string.h>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int maxn=2*50*50+50;
const int inf=1e8;
struct Edge
{
    int u,v,cap,flow;
    Edge(int u,int v,int cap,int flow)
    :u(u),v(v),cap(cap),flow(flow) { }
};
vector<Edge> edges;
vector<int> G[maxn];

void add(int u,int v,int c)
{
    edges.push_back(Edge(u,v,c,0));
    edges.push_back(Edge(v,u,0,0));
    int m=edges.size();
    G[u].push_back(m-2);
    G[v].push_back(m-1);
}

struct ISAP
{
    int d[maxn],num[maxn];
    int cur[maxn],p[maxn];
    int n,s,t;
    void init(int n) { this->n=n; }
    int Augment()
    {
        int x=t,a=inf;
        while(x!=s) {
            Edge&e=edges[p[x]];
            a=min(a,e.cap-e.flow);
            x=e.u;
        }
        x=t;
        while(x!=s) {
            edges[p[x]].flow+=a;
            edges[p[x]^1].flow-=a;
            x=edges[p[x]].u;
        }
        return a;

    }
    int q[maxn],front,rear;
    void bfs()
    {
        for(int i=0; i<n; ++i) d[i]=inf;
        d[t]=0; front=rear=0;
        q[rear++]=t;
        while(front<rear) {
            int u=q[front++];
            for(int i=0; i<G[u].size(); ++i) {
                Edge&e=edges[G[u][i]];
                if(~G[u][i]&1) continue;
            //    printf("%d %d %d %d\n",u,e.v,d[e.v],d[u]);
                if(d[e.v]<=d[u]+1) continue;
                d[e.v]=d[u]+1;
                q[rear++]=e.v;
            }
        }
    }
    int maxflow(int s,int t)
    {
        //printf("%d\n",n);
        this->s=s; this->t=t;
        memset(cur,0,sizeof(cur));
        memset(num,0,sizeof(num));
        bfs();
        for(int i=0;i<n;++i)
        if(d[i]!=inf) ++num[d[i]];
        int x=s,a=0;
        while(d[s]<n) {
            if(x==t) {
                a+=Augment();
                x=s;
            }
            int ok=0;
            for(int&i=cur[x]; i<G[x].size(); ++i) {
                Edge&e=edges[G[x][i]];
                if(e.cap<=e.flow||d[e.v]+1!=d[x]) continue;
                ok=1; p[e.v]=G[x][i];
                x=e.v;
                break;
            }
            if(!ok) {
                int k=n-1;
                for(int i=0; i<G[x].size(); ++i) {
                    Edge&e=edges[G[x][i]];
                    if(e.cap>e.flow) k=min(k,d[e.v]);
                }
                if(--num[d[x]]==0) break;
                ++num[d[x]=k+1];
                cur[x]=0;
                if(x!=s) x=edges[p[x]].u;
            }
        }
        return a;
    }
}solver;


void init()
{
    edges.clear();
    for(int i=0; i<maxn; ++i) G[i].clear();
}

int h,w,s,t,d,f,b;
char pool[55][55];
inline int L(int r,int c) { return 2*(r*w+c+1)-1; }
inline int R(int r,int c) { return 2*(r*w+c+1); }
int Move[2][4]={{-1,0,1,0},{0,1,0,-1}};

void input()
{
    init();
    scanf("%d%d",&w,&h); scanf("%d%d%d",&d,&f,&b);
    s=0; t=2*w*h+1; solver.init(t+1);
    for(int i=0;i<h;++i) scanf("%s",pool[i]);
    for(int i=0;i<h;++i) for(int j=0;j<w;++j)
    {
        add(R(i,j),t,inf);
        if(i==0||j==0||i==h-1||j==w-1) {
            if(pool[i][j]=='.') { add(s,L(i,j),f); add(L(i,j),R(i,j),inf); }
            else { add(s,L(i,j),0); add(L(i,j),R(i,j),inf); }
        } else {
            if(pool[i][j]=='.') { add(s,L(i,j),f); add(L(i,j),R(i,j),0); }
            else { add(L(i,j),R(i,j),d); add(s,L(i,j),0); }
        }
        for(int k=0;k<4;++k) {
            int r=i+Move[0][k];
            int c=j+Move[1][k];
            if(r<0||r>=h||c<0||c>=w) continue;
            add(L(i,j),L(r,c),b);
        }
    }
}

void solve()
{
    printf("%d\n",solver.maxflow(s,t));
}

int main()
{
    int T; cin>>T;
    while(T--) {
        input();
        solve();
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值