【题解】P1034 矩形覆盖

题面

题目传送门

前言

云落的搜索能力实在是太差劲了

正文

其实许多杂七杂八的做法也能过掉这个题

但我们还是选用搜索较好

由题意,矩形数量是给定的 \(k\)

很显然有两种搜索方式

一种是枚举矩形,扩展点(该矩形包含哪些点)

一种是枚举点,扩展矩形(该点属于哪个矩形)

矩形的位置根本无从下手,故舍弃第一种做法

考虑第二种做法如何实现

换言之,怎么去完成 DFS

首先 DFS 一定会有两个参数,分别表示当前面积 \(sum\),以及结点编号 \(cur\)

核心是什么:将当前结点分别加入到 \(k\) 个矩形中

DFS 拓展后回溯

边界条件显然是 \(cur>n\)

细节:

当满足边界条件的时候,需要检查所有矩形是否满足两两不交

状态更新?

假设结点 \(cur\) 加入第 \(i\) 个矩形

\(sum\) 更新:\(当前 sum - 当前矩形 i 的面积 + 加入 cur 后矩形 i 的面积\)

\(cur\) 更新:\(cur+1\)

所以我们需要依照题意,自己搞出来一个数据结构,表示矩形

需要支持如下操作

  1. 记录矩形的坐标位置

  2. 记录其是否被使用

  3. 插入点并扩展矩形坐标

  4. 判断两个矩形是否相交

前三条是好实现的,分别记录 \(ux,dx,uy,dy\) 以及 \(use\) 即可

最后一条稍有难度

考虑构造一个 check 函数,表示一个给定坐标的点是否包含于矩形

注意到矩形 \(A,B\) 的不相交的充要条件是矩形 \(A\) 的四个顶点都不被包含于 \(B\)

随后就能愉快地判断力

代码

#include<iostream>
using namespace std;
const int maxn=55,maxk=5,inf=2147483647;
int n,k,x[maxn],y[maxn],ans;
struct rec{
	int ux,uy,dx,dy;//为左上角,右下角坐标
	bool use;//记录这个矩形中有没有点
	int query(){//求出矩形面积 
		if(!use){
			return 0;//如果还没用过,返回零
		}
		return (dx-ux)*(uy-dy);
	} 
	void Add(int x,int y){//加入点
		//还没有用过的话,就把矩形设成这个点 
		if(!use){
			ux=x;
			uy=y;
			dx=x;
			dy=y;
			use=true;
			return;
		}
		//沿点的方向扩张矩形
		if(ux>x){
			ux=x;
		}
		if(dx<x){
			dx=x;
		} 
		if(uy<y){
			uy=y;
		} 
		if(dy>y){
			dy=y;
		}
	}
	bool check(int x,int y){//判断这个点是否在矩形内 
		if(x<ux||x>dx||y>uy||y<dy){
			return false;
		}
		return true;
	}
	bool cross(rec p){//判断另一个矩形是否与这个矩形相交
		if(!use||!p.use){//如果某一个矩形还没用过,那肯定不相交 
			return false;
		}
		//只要四个顶点中有一个在对方内部,就肯定相交了 
		return p.check(dx,dy)||p.check(dx,uy)||p.check(ux,dy)||p.check(ux,uy);
	}
}g[maxk];
inline bool judge(){
	for(int i=1;i<=k;i++){
		for(int j=i+1;j<=k;j++){//枚举每两个矩形 
			//只要有一个矩形与另一个矩形相交,此方案就不合法
			//我们需要两个都判断因为cross函数只能判断另一个矩形与自己的单向关系 
			if(g[i].cross(g[j])||g[j].cross(g[i])!=0){
				return false;
			}
		}
	}
	return true;
}
inline void dfs(int sum,int cur){//area是当前矩形总面积,cur是当前点的编号
	if(sum>=ans){//最优性剪枝 
		return;
	}
	if(cur>n){//如果n个点全搜完,且没有矩形相交,更新答案并返回 
		if(judge()){
			ans=sum;
		}
		return;
	}
	rec tmp;
	for(int i=1;i<=k;i++){//枚举该把这个点放到哪个矩形里
		tmp=g[i];//记录 
		g[i].Add(x[cur],y[cur]);//把这个点加进去 
		dfs(sum-tmp.query()+g[i].query(),cur+1);//新的面积=原面积-原矩形面积+加入这个点之后的该矩形面积 
		g[i]=tmp;//回溯 
	}
	return;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>x[i]>>y[i];
	}
	ans=inf;
	dfs(0,1);
	cout<<ans<<endl;
	return 0;
}

后记

《天空之城》让云落归于平静……

完结撒花!

posted @ 2025-01-08 14:50  sunxuhetai  阅读(21)  评论(0)    收藏  举报