题意:
有一个n*n的矩阵,其中有m块子矩阵是黑色的.
一次操作你可以选择一个子矩阵,将它涂成白色,
假设子矩阵的边长为h*w,那么代价为min(h,w)。
问将所有黑色格子变成白色的最小代价。
数据范围:n<=1e9,m<=50,给定的子矩阵可能相交
解法:
因为代价是对h和w取min,
那么选择一个矩形可以拆分为选择若干行或者列,显然这样最优,
那么问题可以转化为,一次操作可以选择一行或者一列,
用最少的操作次数,将所有黑点覆盖.
这是一个典型的二分图最小点覆盖问题.
但是题目里面给的不是黑点,而是黑块,
黑点的数量可能特别多,如果暴力建图肯定不行.
可以将每一个黑块看作一个黑点,也就是进行离散化,
这样之后每个黑点需要的操作次数就不是1了,而是min(h,w),
那么问题就不是最小点覆盖而是最小点权覆盖了,
用网络流解决.
建图:
S->行点,边权为行宽
列点->T,边权为列宽
行点->对应列点,边权inf
关于代码中矩阵左上角坐标为什么要减1:
把坐标变成左闭右开,
例如有区间[2,3]和[4,5],实际上这两个区间是连接的,
将它改成(1,3]和(3,5],这样中间3的位置就相交了,
离散化之后只有1,3,5而不是1,2,4,5
那么两区间代价直接就可以(3-1)和(5-3),即相邻差值,比较方便
如果不这样做,代价为(3-2+1)和(5-4+1),而且没有(4-3+1),需要判断
(当然,也可以是右端点+1)
code:
#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define ll long long
//#define ull unsigned long long
typedef pair<int,int> PI;
const int maxm=1e5+5;
struct Dinic{
static const int DN=1e5+5;
static const int DM=4e5+5;
static const long long inf=1e15;
int head[DN],nt[DM],to[DM];
long long w[DM],cnt=1;
int d[DN];
int st,ed;
long long maxflow;
int idx;
void init(){
for(int i=0;i<=idx;i++)head[i]=0;
cnt=1;
idx=0;
}
void add(int x,int y,long long z){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void add2(int x,int y,long long z){
idx=max(idx,max(x,y));
add(x,y,z);
add(y,x,0);
}
bool bfs(){
queue<int>q;
q.push(st);
for(int i=0;i<=idx;i++)d[i]=0;
d[st]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nt[i]){
int v=to[i];
if(w[i]&&!d[v]){
d[v]=d[x]+1;
q.push(v);
if(v==ed)return 1;
}
}
}
return 0;
}
long long dfs(int x,long long flow){
if(x==ed)return flow;
long long res=flow;
for(int i=head[x];i;i=nt[i]){
int v=to[i];
if(w[i]&&d[v]==d[x]+1){
int k=dfs(v,min(res,(long long)w[i]));
w[i]-=k;
w[i^1]+=k;
res-=k;
if(!k)d[v]=-1;
if(!res)break;
}
}
return flow-res;
}
long long dinic(){
maxflow=0;
while(bfs()){
maxflow+=dfs(st,inf);
}
return maxflow;
}
}G;
struct Node{
int x,y,xx,yy;
void input(){
scanf("%d%d%d%d",&x,&y,&xx,&yy);
}
}a[maxm];
int n,m;
int x1[maxm],num1;
int y1[maxm],num2;
signed main(){
G.init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
a[i].input();
a[i].x--;
a[i].y--;
x1[++num1]=a[i].x;
x1[++num1]=a[i].xx;
y1[++num2]=a[i].y;
y1[++num2]=a[i].yy;
}
sort(x1+1,x1+1+num1);
sort(y1+1,y1+1+num2);
num1=unique(x1+1,x1+1+num1)-x1-1;
num2=unique(y1+1,y1+1+num2)-y1-1;
for(int i=1;i<=m;i++){
a[i].x=lower_bound(x1+1,x1+1+num1,a[i].x)-x1;
a[i].xx=lower_bound(x1+1,x1+1+num1,a[i].xx)-x1;
a[i].y=lower_bound(y1+1,y1+1+num2,a[i].y)-y1;
a[i].yy=lower_bound(y1+1,y1+1+num2,a[i].yy)-y1;
for(int x=a[i].x;x<a[i].xx;x++){
for(int y=a[i].y;y<a[i].yy;y++){
G.add2(x,y+num1,G.inf);
}
}
}
G.st=++G.idx;
G.ed=++G.idx;
for(int i=1;i<num1;i++){
G.add2(G.st,i,x1[i+1]-x1[i]);
}
for(int i=1;i<num2;i++){
G.add2(i+num1,G.ed,y1[i+1]-y1[i]);
}
ll ans=G.dinic();
printf("%lld\n",ans);
return 0;
}