题意:网格图上面有一个区域,你要用最少的不相交的矩形精确覆盖整个区域。
我们将精确覆盖看成切割,也就是将这个区域切成最少的矩形。
首先,我们将270°的角称为凹点,90°的角称为凸点,很明显我们的目标是要把全部的凹点切掉,剩下的全都是凸点,那就一定是若干个矩形。
接着就要考虑凹点的切割方法(切的任何一条边都一定有一个点是凹点,否则没有意义),两种情况:1、切的两个都是凹点;2、切的一个是凹点,另外一个是一条边。
对于情况1,我们会发现可以直接把两个凹点变成普通的凸点,不会影响区域的总点数;对于情况2,会把一个凹点转化成两个凸点。
因为我们要让最后剩下的凸点最少,所以肯定选择1会比2更加优秀,也就是找到凹点-凹点切割的最大次数,做法就是将所有的切割方法先预处理出来,因为可以分为横切和竖切,如果某个横切和某个竖切相交,就连边,因此可以建立二分图,然后跑最大独立集即可。
我们设 s 1 s_1 s1是区域顶点的总个数, s 2 s_2 s2是区域凹点的个数, s 3 s_3 s3是最大独立集大小。
那么我们切完凹点-凹点的刀以后,剩下的凹点就是 s 2 − 2 s 3 s_2-2s_3 s2−2s3,总顶点个数不变。
再切完剩下的凹点以后,总顶点的个数是 s 1 + 2 ( s 2 − 2 s 3 ) s_1+2(s_2-2s_3) s1+2(s2−2s3),因此剩下的一定是 1 4 ( s 1 + 2 s 2 − 4 s 3 ) \frac{1}{4}(s1+2s_2-4s_3) 41(s1+2s2−4s3)个矩形。
需要注意的是,这个顶点数计算的时候需要考虑顶点相交的矩形的情况,这种情况要顶点应该要算两个才行。
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){x=0;int f=0;char s=getchar();while(!isdigit(s))f|=s=='-',s=getchar();while(isdigit(s))x=x*10+s-48,s=getchar();x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){if(x<0)putchar('-'),x=-x;do{buf[++cc]=int(x%10);x/=10;}while(x);while(cc)putchar(buf[cc--]+'0');
}
namespace DIC{
const int N=4e3+10,M=600*600+N*N+10;
int h[N],st,ed,cur[N];
int tot=1,hd[N],ver[M<<1],nxt[M<<1],w[M<<1];
void add(int x,int y,int z){tot++;ver[tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
void link(int x,int y,int z){add(x,y,z),add(y,x,0);
}
bool bt_h(){memset(h,0,sizeof(h));h[st]=1;queue<int>q;q.push(st);while(q.size()){int x=q.front();q.pop();for(int i=hd[x];i;i=nxt[i]){int y=ver[i];if(w[i]&&!h[y]){h[y]=h[x]+1;q.push(y);}}}return h[ed]!=0;
}
int findflow(int x,int f){if(x==ed)return f;int res=f,tt;for(int &i=cur[x];i;i=nxt[i]){int y=ver[i];if(w[i]&&h[y]==h[x]+1){tt=findflow(y,min(res,w[i]));w[i]-=tt,w[i^1]+=tt;res-=tt;if(!res)break;}}if(res==f)h[x]=0;return f-res;
}
int dicnic(){int ans=0;while(bt_h()){memcpy(cur,hd,sizeof(cur));ans+=findflow(st,1e9);}return ans;
}
}
const int N=610;
struct node{int x1,y1,x2,y2;
}a[N];int n;
int cnt,s[N];
int sum[N][N],id[N][N];
int tot1,tot2;
bool pd(int x,int y){int res=0;rep(i,-1,0)rep(j,-1,0)if(sum[x+i][y+j]>0)res++;return res==3;
}
int pd_vertex(int x,int y){int res=0;rep(i,-1,0)rep(j,-1,0)if(sum[x+i][y+j]>0)res++;if(res&1)return 1;if(!res)return 0;if(res==4)return 0;if((sum[x][y]>0)==(sum[x-1][y-1]>0))return 2;return 0;
}
void solve(){qr(n);rep(i,1,n){qr(a[i].x1),qr(a[i].y1);qr(a[i].x2),qr(a[i].y2);a[i].x2++,a[i].y2++;s[++cnt]=a[i].x1;s[++cnt]=a[i].x2;}sort(s+1,s+cnt+1);cnt=unique(s+1,s+cnt+1)-s-1;rep(i,1,n){a[i].x1=lower_bound(s+1,s+cnt+1,a[i].x1)-s;a[i].x2=lower_bound(s+1,s+cnt+1,a[i].x2)-s;}cnt=0;rep(i,1,n){s[++cnt]=a[i].y1;s[++cnt]=a[i].y2;}sort(s+1,s+cnt+1);cnt=unique(s+1,s+cnt+1)-s-1;rep(i,1,n){a[i].y1=lower_bound(s+1,s+cnt+1,a[i].y1)-s;a[i].y2=lower_bound(s+1,s+cnt+1,a[i].y2)-s;}rep(i,1,n){sum[a[i].x1][a[i].y1]++;sum[a[i].x1][a[i].y2]--;sum[a[i].x2][a[i].y1]--;sum[a[i].x2][a[i].y2]++;}rep(i,1,2*n)rep(j,1,2*n){sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];}
// rep(i,1,10){
// rep(j,1,10){
// if(sum[i][j]>0)putchar('1');
// else putchar('0');
// }
// puts("");
// }rep(i,1,2*n){int last=-1;rep(j,1,2*n){if(pd(i,j)){if(last!=-1){tot1++;rep(k,last,j)id[i][k]=tot1;}else{last=j;}}if((sum[i][j]>0)+(sum[i-1][j]>0)<2)last=-1;}}rep(j,1,2*n){int last=-1;rep(i,1,2*n){if(pd(i,j)){if(last!=-1){tot2++;rep(k,last,i)if(id[k][j]){DIC::link(id[k][j],tot1+tot2,1);}}else{last=i;}}if((sum[i][j]>0)+(sum[i][j-1]>0)<2)last=-1;}}DIC::st=tot1+tot2+1;DIC::ed=DIC::st+1;rep(i,1,tot1)DIC::link(DIC::st,i,1);rep(i,1,tot2)DIC::link(tot1+i,DIC::ed,1);int R,P=0,C=0,nn=tot1+tot2-DIC::dicnic();rep(i,1,2*n)rep(j,1,2*n){P+=pd_vertex(i,j);if(pd(i,j))C++;}R=(P+2*C-4*nn)/4;qw(R);puts("");
}
int main(){int tt;tt=1;while(tt--)solve();return 0;
}