洛谷P1034 【矩形覆盖】
Dfs+剪枝 保证正确性
复杂度:\(O(玄学)\)
此题标签是计算几何,可以极角排序然后DP,然而我因为想练搜索写了这份代码
对于每一个点,我们枚举它在哪一个矩形中。即对于每一个点,可以创建一个新矩阵储存它,也可以用旧的矩阵储存它。我们可以用vector来储存每个矩形中的点,当矩形中的点确定,矩形的大小和位置就是唯一确定的。
当n个点被放入k个矩形中时,我们计算出矩形的面积总和并更新答案。因为矩形不能重合,而矩形重合当且仅当一个矩形的一个顶点至少一个在另一个矩形中,两两枚举判断即可
光这样是无法AC此题的,我们还需要剪枝。显然随着点数的增加,矩形面积单调递增。所以我们每次计算出矩形的面积判断是否比答案优,即可AC此题
还有一个小剪枝,如果当前使用矩阵+未使用的点数<k,可以直接return
枚举K的做法只能通过K<=3的数据,不够严谨,而DP需要计算几何知识,对蒟蒻不友好,还是深搜最有保证
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
using namespace std;
#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
const int N=55,inf=0x3f3f3f3f;
int n,K,x1[5],y1[5],x2[5],y2[5],x[N],y[N],last=inf;
vector<int>sq[5];
inline void read(int &x){
x=0;char f=1,c=getchar();
while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
x*=f;
}
inline bool pd(int i,int j){
if(x1[i]>=x1[j]&&x1[i]<=x2[j]&&y1[i]>=y1[j]&&y1[i]<=y2[j]) return 0;
if(x2[i]>=x1[j]&&x2[i]<=x2[j]&&y1[i]>=y1[j]&&y1[i]<=y2[j]) return 0;
if(x2[i]>=x1[j]&&x2[i]<=x2[j]&&y2[i]>=y1[j]&&y2[i]<=y2[j]) return 0;
if(x1[i]>=x1[j]&&x1[i]<=x2[j]&&y2[i]>=y1[j]&&y2[i]<=y2[j]) return 0;
return 1;
}
inline int sum(int num){
mem(x1,0x3f),mem(y1,0x3f);
mem(x2,0),mem(y2,0);
go(i,1,num-1)
for(int j=0;j<sq[i].size();++j){
x1[i]=min(x1[i],x[sq[i][j]]);
y1[i]=min(y1[i],y[sq[i][j]]);
x2[i]=max(x2[i],x[sq[i][j]]);
y2[i]=max(y2[i],y[sq[i][j]]);
}
go(i,1,num-1)
go(j,i+1,num-1){
if(!pd(i,j)) return inf;
}
static int ans;
ans=0;
go(i,1,num-1) ans+=(x2[i]-x1[i])*(y2[i]-y1[i]);
return ans;
}
void dfs(int p,int num){
static int ans;
if((ans=sum(num))>=last) return;
if(n-p+num<K) return;
if(p==n+1&&num==K+1){ if(ans<last) last=ans; } return; }
if(num<=K){
sq[num].push_back(p);
dfs(p+1,num+1);
sq[num].pop_back();
}
if(num>1){
go(i,1,num-1){
sq[i].push_back(p);
dfs(p+1,num);
sq[i].pop_back();
}
}
}
int main(){
//freopen("input.txt","r",stdin);
read(n),read(K);
go(i,1,n) read(x[i]),read(y[i]);
dfs(1,1);
printf("%d",last);
return 0;
}