[HAOI2007][洛谷P2218]覆蓋問題

萝卜甜了發表於2024-03-20

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;
}

相關文章