Codeforces Round #576 (Div. 1) E. Rectangle Painting 2 最大流

100 篇文章 0 订阅

题目链接: http://codeforces.com/contest/1198/problem/E

题意:

在一个 1 e 9 1e9 1e9 的二维平面上,给你 50 50 50 个黑色的矩形(可以覆盖),现在你可以选择若干个矩形区域涂白,每个矩形的花费是 m i n ( h , w ) min(h,w) min(h,w) ,即该矩形的长宽中较小的值,问你将黑色的部分全部涂白的最小花费。

做法:

这个和上一道涂白很不同的一点就是这道题的矩形花费是长宽中较小的值,在这个基础上求最小,这也是这道题为什么可以用网络流的原因,(其实一开始我也想不到网络流怎么做…),这样的情况下,对于一个固定的矩形,我们可以将它拆成两个点,一个是行标,一个是列标,分别向源点和汇点连边,流量就是他们的长度,然后如果这个矩形存在,那么我们就将这个行标和这个列标连一个无穷的流,表示这个矩形我一定要取。

由于要的是长宽中较小的,所以其实一个因为重复而形成的多边形还是要尽量拆掉来做的。

可能有的人还看不太懂是什么意思,我就又画了下面的图(有点丑)

比如下面图中的 234 234 234 部分组成的图形,不是包括了右上角最优,而是将 23 23 23 4 4 4单独分开来做会更好,所以我们就可以将所有的行和列离散化,每个行代表的是其包括了上面的区域,每个列是其包括了左边的区域,,即行 2 2 2 可以包括区域 12 12 12

在这里插入图片描述
然后我们就可以将整个图划分为100*100个部分,如果这个矩形中是黑的,那么就给对应的行和列连一条边,然后跑个最大流就好了。

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
const int maxn=350;
const int maxm=35000;
const int MAX = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
int nex[maxm],to[maxm],cap[maxm],from[maxm];
int n,m,cnt,head[maxn],d[maxn],sp,tp;//原点,汇点
//理论复杂度n2*m
void add(int u,int v,int c){
    //printf("from %d to %d cap %d\n",u,v,c);
    from[cnt]=u,to[cnt]=v,cap[cnt]=c,nex[cnt]=head[u],head[u]=cnt++;
    from[cnt]=v,to[cnt]=u,cap[cnt]=0,nex[cnt]=head[v],head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=nex[i]){
            int u=to[i];
            if(d[u]==-1 && cap[i]>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
int dfs(int a,int b){
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=nex[i]){
        int u=to[i];
        if(cap[i]>0 && d[u]==d[a]+1){
            int x=min(cap[i],b-r);
            x=dfs(u,x);
            r+=x;
            cap[i]-=x;
            cap[i^1]+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}
int dinic(int sp,int tp){
    int total=0,t;
    while(bfs()){
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
bool vis[110][110];
int tx[110],ty[110],nx,ny;
int x[110],y[110];
int gain(int tmp[],int tn,int x){
    return lower_bound(tmp+1,tmp+1+tn,x)-tmp;
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    rep(i,1,m){
        int pos=i*2-1;
        scanf("%d%d%d%d",&x[pos],&y[pos],&x[pos+1],&y[pos+1]);
        x[pos]--,y[pos]--;
        tx[pos]=x[pos],tx[pos+1]=x[pos+1],ty[pos]=y[pos],ty[pos+1]=y[pos+1];
    }
    tx[m*2+1]=0,tx[m*2+2]=n;
    ty[m*2+1]=0,ty[m*2+2]=n;
    sort(tx+1,tx+1+2*m+2);
    sort(ty+1,ty+1+2*m+2);
    nx=unique(tx+1,tx+1+2*m+2)-tx-1;
    ny=unique(ty+1,ty+1+2*m+2)-ty-1;
    sp=0,tp=110;
    rep(i,1,nx-1) add(sp,i,tx[i+1]-tx[i]);
    rep(i,1,ny-1) add(i+110,tp,ty[i+1]-ty[i]);

    rep(k,1,m){
        int pos=k*2-1;
        int xl=gain(tx,nx,x[pos]),xh=gain(tx,nx,x[pos+1]);
        int yl=gain(ty,ny,y[pos]),yh=gain(ty,ny,y[pos+1]);
        rep(i,xl,xh-1){
            rep(j,yl,yh-1){
                vis[i][j]=1;
            }
        }
    }
    rep(i,1,nx-1){
        rep(j,1,ny-1){
            if(vis[i][j]) add(i,j+110,MAX);
        }
    }

    printf("%d\n",dinic(sp,tp));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值