【题解】P1034 矩形覆盖
题面
前言
云落的搜索能力实在是太差劲了
正文
其实许多杂七杂八的做法也能过掉这个题
但我们还是选用搜索较好
由题意,矩形数量是给定的 \(k\)
很显然有两种搜索方式
一种是枚举矩形,扩展点(该矩形包含哪些点)
一种是枚举点,扩展矩形(该点属于哪个矩形)
矩形的位置根本无从下手,故舍弃第一种做法
考虑第二种做法如何实现
换言之,怎么去完成 DFS
首先 DFS 一定会有两个参数,分别表示当前面积 \(sum\),以及结点编号 \(cur\)
核心是什么:将当前结点分别加入到 \(k\) 个矩形中
DFS 拓展后回溯
边界条件显然是 \(cur>n\)
细节:
当满足边界条件的时候,需要检查所有矩形是否满足两两不交
状态更新?
假设结点 \(cur\) 加入第 \(i\) 个矩形
\(sum\) 更新:\(当前 sum - 当前矩形 i 的面积 + 加入 cur 后矩形 i 的面积\)
\(cur\) 更新:\(cur+1\)
所以我们需要依照题意,自己搞出来一个数据结构,表示矩形
需要支持如下操作
-
记录矩形的坐标位置
-
记录其是否被使用
-
插入点并扩展矩形坐标
-
判断两个矩形是否相交
前三条是好实现的,分别记录 \(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;
}
后记
《天空之城》让云落归于平静……
完结撒花!