CF #576 Div.1 E. Rectangle Painting 2 //最大流(最小点覆盖)

题意:
在这里插入图片描述
在这里插入图片描述
思路:
每个点只需要把宽或者长对应的染黑,那么就是最小点覆盖问题。
离散化,将所有边都延长割成网状,割成一个个小矩形,左闭右开统计大矩阵由小矩阵组成。

最小点覆盖 → \rightarrow 二分图最大匹配 → \rightarrow 最大流

建图:长 → \rightarrow 宽 的边权为 i n f inf inf,其他边权为对应的长度

源点 → \rightarrow 离散化后的一段段x → \rightarrow 离散化后的一段段y → \rightarrow 汇点

跑最大流求最小覆盖

不愧*2500

/*   Author : Rshs
 *   Data : 2019-09-11-20.16
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define int long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const int mod = 1e9+7;
const int MX = 1e5+5;
//****************************************************
const int max_n = 500;
struct no{int to,cap,rev;}; //arc
vector<no>g[max_n]; //图
int level[max_n]; //到起点的距离
int iter[max_n]; //当前弧,在其之前的边已经没用了
void addarc(int s,int e,int c){
	g[s].push_back((no){e,c,(int)g[e].size()});
	g[e].push_back((no){s,0,(int)g[s].size()-1});
}
//更新层次,即level
void bfs(int s){
	memset(level,-1,sizeof(level));
	level[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int now=q.front();q.pop();
		for(int i=0;i<(int)g[now].size();i++){
			no &arc=g[now][i];
			if(level[arc.to]!=-1||arc.cap<=0) continue;
			level[arc.to]=level[now]+1;
			q.push(arc.to);
		}
	}
}
//寻找增广路
int dfs(int v,int t,int f){
	if(v==t) return f;
	for(iter[v];iter[v]<(int)g[v].size();iter[v]++){
		no &arc=g[v][iter[v]];
		if(arc.cap<=0||level[arc.to]!=level[v]+1) continue;
		int d=dfs(arc.to,t,min(f,arc.cap));
		if(d>0) {
			arc.cap=arc.cap-d;
			g[arc.to][arc.rev].cap+=d;
			return d;
		}
	}
	return 0;
}
int Dinic(int s,int t){
	int re=0;
	while(1){
		bfs(s);
		memset(iter,0,sizeof(iter));
		if(level[t]==-1) return re;
		int f;
		while((f=dfs(s,t,LLONG_MAX))>0)
			re=re+f;
	}
	return re;
}
//****************************************************
int a[MX],b[MX],x[MX],y[MX];
vector<int>lx,ly;
int vis[200][200];
int main(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>a[i]>>b[i]>>x[i]>>y[i];
        x[i]++,y[i]++;
        lx.push_back(a[i]);lx.push_back(x[i]);
        ly.push_back(b[i]),ly.push_back(y[i]);
    }
    sort(lx.begin(),lx.end());sort(ly.begin(),ly.end());
    lx.erase(unique(lx.begin(),lx.end()),lx.end());
    ly.erase(unique(ly.begin(),ly.end()),ly.end());
    int sx=SZ(lx),sy=SZ(ly);int SS=0,TT=sx+sy+1;
    for(int i=1;i<sx;i++) addarc(SS,i,lx[i]-lx[i-1]); //左闭又开,S连边
    for(int i=1;i<sy;i++) addarc(i+sx,TT,ly[i]-ly[i-1]);
    for(int i=1;i<=m;i++){
        int l1,l2,r1,r2;
        l1=lower_bound(lx.begin(),lx.end(),a[i])-lx.begin()+1;
        l2=lower_bound(lx.begin(),lx.end(),x[i])-lx.begin()+1;
        r1=lower_bound(ly.begin(),ly.end(),b[i])-ly.begin()+1;
        r2=lower_bound(ly.begin(),ly.end(),y[i])-ly.begin()+1;
        for(int j=l1;j<l2;j++){
            for(int k=r1;k<r2;k++){
                vis[j][k]=1;
            }
        }
    }
    for(int j=1;j<sx;j++){
        for(int k=1;k<sy;k++){
            if(vis[j][k]) addarc(j,k+sx,LLONG_MAX/10000);
        }
    }
    cout<<Dinic(SS,TT);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值