[HAOI2007][洛谷P2218] 覆盖问题

image

看到这道题,思考一下后发现要用二分答案。所以为什么要用二分?
因为标签有二分还在二分专题里
因为对于 \(ans\) 来说,如果 \(ans\) 不行,那么 \(ans-1\) 也一定不行;也就是说,答案满足单调性,所以可以二分;
也是因为暴力明显过不了

那么对于平面上的一些点来说,如果我们用一个最小的矩形覆盖所有点,那么这个矩形的大小和位置都是固定的;

所以我们的目标就是三个等大的小正方形替换它,并保证所有点仍被覆盖;

由于小正方形的边与坐标轴平行,而这个最小的矩形的每个边上都至少有一个点(否则不满足“最小”),所以每个小正方形一定至少有一条边与大长方形重合;

而由于我们只有三个正方形,所以一定有一个正方形在角上;

到这里,思路已经很清晰了:
二分小正方形边长,dfs 四个角的四种情况,得出答案

完整代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+5;
const int inf=0x7fffffff;
struct tree{
	int x,y,now;
	bool operator < (const tree &a)const{//优先按照 x 升序,x 相等按 y 升序 
		if(x==a.x)return y<a.y;
		return x<a.x;
	}
}a[N];
int n,maxn;
void cap(int minx,int maxx,int miny,int maxy,int now){//对指定区间进行覆盖,now记录当前是第几块塑料布 
	for(int i=1;i<=n;i++){
		if(a[i].now)continue;
		if(minx<=a[i].x&&a[i].x<=maxx&&miny<=a[i].y&&a[i].y<=maxy)
			a[i].now=now;
	}
}
void recap(int now){//取消点当前的now标记 (之前的不会清空) 
	for(int i=1;i<=n;i++){
		if(a[i].now==now)a[i].now=0;
	}
}
bool dfs(int now,int mid){
	int minx=inf,maxx=-inf;
	int miny=inf,maxy=-inf;
	for(int i=1;i<=n;i++){
		if(!a[i].now){//在未被覆盖的点中寻找最大与最小 x y 坐标 
			maxx=max(maxx,a[i].x); 
			minx=min(minx,a[i].x);
			maxy=max(maxy,a[i].y);
			miny=min(miny,a[i].y);
		}
	}
	int lenx=maxx-minx;
	int leny=maxy-miny;
	if(max(lenx,leny)<=mid)return 1;//能够覆盖 
	if(now==3)return 0;//塑料布用完了 
	
	cap(minx,minx+mid,miny,miny+mid,now);//左下角 
	if(dfs(now+1,mid))return 1;//寻找下一块 
	recap(now);//回溯 
	
	cap(minx,minx+mid,maxy-mid,maxy,now);//左上角 
	if(dfs(now+1,mid))return 1;
	recap(now);
	
	cap(maxx-mid,maxx,miny,miny+mid,now);//右下角 
	if(dfs(now+1,mid))return 1;
	recap(now);
	
	cap(maxx-mid,maxx,maxy-mid,maxy,now);//右上角 
	if(dfs(now+1,mid))return 1;
	recap(now);
	
	return 0;
}
bool check(int x){
	for(int i=1;i<=n;i++)a[i].now=0;//清空覆盖状态 
	return dfs(1,x);//dfs 
}
int solve(int l,int r){//喜欢打递归版的二分答案 
	int mid=(l+r)>>1;
	if(r<=l)return mid;
	if(check(mid))return solve(l,mid);
	else return solve(mid+1,r);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].x>>a[i].y;
	}
	sort(a+1,a+1+n);//记得排序 
	cout<<solve(1,inf);
	return 0;
}
posted @ 2024-03-20 11:33  萝卜甜了  阅读(15)  评论(0编辑  收藏  举报