矩形分割(二分答案or二分搜索)

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;
}
原创文章 34 获赞 87 访问量 2723

猜你喜欢

转载自blog.csdn.net/qq_44643644/article/details/105863864
今日推荐