题目
农夫约翰希望为他的奶牛们建立一个畜栏。
这些挑剔的畜生要求畜栏必须是正方形的,而且至少要包含C单位的三叶草,来当做它们的下午茶。
畜栏的边缘必须与X,Y轴平行。
约翰的土地里一共包含N单位的三叶草,每单位三叶草位于一个1 x 1的土地区域内,区域位置由其左下角坐标表示,并且区域左下角的X,Y坐标都为整数,范围在1到10000以内。
多个单位的三叶草可能会位于同一个1 x 1的区域内,因为这个原因,在接下来的输入中,同一个区域坐标可能出现多次。
只有一个区域完全位于修好的畜栏之中,才认为这个区域内的三叶草在畜栏之中。
请你帮约翰计算一下,能包含至少C单位面积三叶草的情况下,畜栏的最小边长是多少。
输入格式
第一行输入两个整数 C 和 N。
接下来 N 行,每行输入两个整数 X 和 Y,代表三叶草所在的区域的X,Y坐标。
同一行数据用空格隔开。
输出格式
输出一个整数,代表畜栏的最小边长。
数据范围
1≤C≤500
C≤N≤500
输入样例
3 4
1 2
2 1
4 1
5 2
输出样例
4
分析
- 这个题目我们可以二分最小边长,然后用二维前缀和判断边长为mid矩形区域有没有大于c的三叶草。
- 但是因为x,y的范围是1到10000,所以如果我们暴力枚举顶点的话复杂度会很高,我们发现n的范围只有500,因此假如每个三叶草的位置都不同,也只有1000个不同的x、y,因此我们可以将x,y离散化,排序后用下标代表它,这样我们就从枚举10000 * 10000降到了枚举1000 * 1000.
- 具体实现看代码
代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N=505;
int sum[N*2][N*2];
vector<int> num;
pair<int,int> p[N];
int n,c;
int get(int x){ //获取离散化后x对应的值,即他的下标
return lower_bound(num.begin(),num.end(),x)-num.begin();
}
bool check(int x){ //判断边长为x是否满足条件
for(int x2=1,x1=1;x2<num.size();x2++){
while(num[x2]-num[x1]+1>x) x1++;
for(int y2=1,y1=1;y2<num.size();y2++){
while(num[y2]-num[y1]+1>x) y1++;
if(sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]>=c){
return true;
}
}
}
return false;
}
int main()
{
cin>>c>>n;
num.push_back(0);
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
p[i]={x,y};
num.push_back(x);
num.push_back(y);
}
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());//去除重复的x,y
for(int i=0;i<n;i++){
int x=get(p[i].first),y=get(p[i].second);//取得x、y离散化后的值
// cout<<x<<" "<<y<<endl;
sum[x][y]++;
}
for(int i=1;i<=num.size();i++){ //前缀和
for(int j=1;j<=num.size();j++){
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
// cout<<sum[i][j]<<" ";
}
// cout<<endl;
}
int l=1,r=10000;
while(l<r){//二分求边长
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<r<<endl;
return 0;
}