Description
平面上有一个大矩形,其左下角坐标 (0,0),右上角坐标 (R,R)。大矩形内部包含一些小矩形,小矩形都平行于坐标轴且互不重叠。所有矩形的顶点都是整点。要求画一根平行于y轴的直线x=k( k 是整数),使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。并且,要使得大矩形在直线左边的的面积尽可能大。注意:若直线穿过一个小矩形,将会把它切成两个部分,分属左右两侧。
Input
第一行是整数 R,表示大矩形的右上角坐标是 (R,R)。
接下来的一行是整数 N,表示一共有 N个小矩形。
再接下来有N 行。每行有4个整数 L T W H
表示有一个小矩形的左上角坐标是 (L,T) ,宽度是W,高度是 H.
小矩形不会有位于大矩形之外的部分。
Output
输出整数 n ,表示答案应该是直线 x=n 。 如果必要的话,x=R 也可以是答案。
Sample Input 1
1000
2
1 1 2 1
5 1 2 1
Sample Output 1
5
Hint
1≤ R≤ 10^6
0<N≤ 10000
0≤L,T≤ R,0≤W,H≤ R
Time Limit
1000MS
Memory Limit
256MB
分析题意:
要找的答案x本身就是单调的,我们不可能一个一个x的枚举计算,这样会超时,所以得把答案二分再搜索。
答案必须满足要求:
1.这些小矩形落在直线左边的面积必须大于等于落在右边的面积
2.两边面积之差最小
3.大矩形在直线左边的的面积尽可能大
可以发现题目其实想让我们找一条分界线,这条分界线,使左右小矩形面积差最小(最接近0),且过了这条分界线,左右小矩形面积差就不是最小的了。如果我们换个角度看问题,把直线x=k左边的小矩形面积总和看作一个数组,k为下标,我们要在这个有限递增的数组中找一个值:不小于所有小矩形总面积1/2,且最接近所有小矩形总面积1/2。同时,在元素可能重复的情况下,我们要找到符合要求的最后一个元素。这不就回归到二分查找了吗!二分查找可能重复元素的最后一个。只不过我们所查找的数组是通过我们自己计算,且不必计算所有元素。——这就是二分查找和二分答案的区别。
另外,由于题目的数据量比较大,此题应该开long long int。
#include<stdio.h>
#include<algorithm>
using namespace std;
struct rectangle//记录小矩形的各个数据
{//横竖坐标、宽、高
long long int l,t,w,h;
}rtg[10000];//小矩形数组
struct cmp
{//矩形越左排位越前
bool operator()(rectangle &r1,rectangle &r2)
{
return r1.l<r2.l;
}
};
//数据量大,开long long
//大矩形坐标R
//当前解x=ans左边小矩形的面积left_area
//所有小矩形总面积sum_area
//答案ans
long long int R,left_area=0,sum_area=0,ans;
int N;//小矩形数目
//求x=k左侧小矩形的总面积
long long int sum(long long int k)
{
long long int sum=0;
for(int i=0;i<N;i++)
{
if(rtg[i].l+rtg[i].w<=k)
sum+=rtg[i].w*rtg[i].h;
else if(rtg[i].l<k && rtg[i].l+rtg[i].w>k)
sum+=(k-rtg[i].l)*rtg[i].h;
else break;//已经按"越左越前"排过序,可以直接退出
}
return sum;
}
int main()
{
scanf("%lld%d",&R,&N);
for(int i=0;i<N;i++)
{
scanf("%lld%lld%lld%lld",&rtg[i].l,&rtg[i].t,&rtg[i].w,&rtg[i].h);
//顺便计算所有小矩形面积和sun_area
//因为之后要比较两次搜索左边小矩形面积和的大小,
//所以给left_area赋初值为最大值
left_area=sum_area+=rtg[i].w*rtg[i].h;
}
sort(rtg,rtg+N,cmp());//排序
//x=mid左边的小矩形总面积temp_left
//左开右开区间(0,R+1),解绝对不可能是0、R+1
long long int left=0,right=R+1,mid,temp_left;
while(left+1!=right)//左开右开对应的结束边界
{ //因为要找最后一个元素,故mid不妨向上取整
mid=left+((right-left+1)>>1);
temp_left=sum(mid);
if(2*temp_left==sum_area){
ans=mid;
left_area=temp_left;
//因为要找最后一个,所以在(mid,right)继续搜索
left=mid;
}
else if(2*temp_left>sum_area){
//找到更小面积差的位置,则记录答案
if(temp_left<left_area){
ans=mid;
left_area=temp_left;
}
//左边大了,区间左移,排除mid,区间变为(left,mid)
right=mid;
}
//左边太小,区间右移,排除mid,区间变为(mid,right)
else left=mid;
}
//保险起见,检验找到元素是否是重复元素中最后一个元素,若不是则调整
while(ans<R && left_area==sum(ans+1)) ++ans;
printf("%lld",ans);
return 0;
}