• 10B:矩形分割


    总时间限制: 
    1000ms
     
    内存限制: 
    65536kB
    描述

    平面上有一个大矩形,其左下角坐标(0,0),右上角坐标(R,R)。大矩形内部包含一些小矩形,小矩形都平行于坐标轴且互不重叠。所有矩形的顶点都是整点。要求画一根平行于y轴的直线x=k(k是整数) ,使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。并且,要使得大矩形在直线左边的的面积尽可能大。注意:若直线穿过一个小矩形,将会把它切成两个部分,分属左右两侧。

    输入
    第一行是整数R,表示大矩形的右上角坐标是(R,R) (1 <= R <= 1,000,000)。
    接下来的一行是整数N,表示一共有N个小矩形(0 < N <= 10000)。
    再接下来有N 行。每行有4个整数,L,T, W 和 H, 表示有一个小矩形的左上角坐标是(L,T),宽度是W,高度是H (0<=L,T <= R, 0 < W,H <= R). 小矩形不会有位于大矩形之外的部分。
    输出
    输出整数n,表示答案应该是直线 x=n。 如果必要的话,x=R也可以是答案。
    样例输入
    1000
    2
    1 1 2 1
    5 1 2 1
    样例输出
    5

    代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 
     5 int area[1000005]; //每一列的小矩形的面积,如area[0]表示横坐标为0到1之间的小矩形的面积 
     6 int R; //题目中的R
     7 int N; //题目中的N
     8 
     9 long long check(int mid){
    10     long long left=0,right=0; //注意总面积很有可能超出2^31
    11     for(int i=0; i<mid; i++) {
    12         left+=area[i];
    13     }
    14     for(int i=mid; i<R; i++) {
    15         right+=area[i];
    16     }
    17     return left-right;
    18 } 
    19 int main() {
    20     scanf("%d%d",&R,&N);
    21     for(int i=0; i<N; i++) {//预处理 
    22         int l,t,w,h;
    23         scanf("%d%d%d%d",&l,&t,&w,&h);
    24         for(int i=l; i<l+w; i++) {
    25             area[i]+=h;
    26         }
    27     }
    28     long long l,r,mid, ans;
    29     l=0;
    30     r=R;
    31     while(r>l) {
    32         mid=(l+r)/2;
    33         //cout<<l<<" "<<r<<" "<<mid<<endl;
    34         long long temp = check(mid);
    35         if(!temp) { //左面积等于右面积,这时候就往右移 
    36             ans = mid;
    37             break;
    38         }
    39         else if(temp<0){ //这样不行 
    40             l = mid+1;
    41         }
    42         else{
    43             r = mid;
    44             ans = mid;
    45         } 
    46     }
    47     long long left=0,right=0; //注意总面积很有可能超出2^31
    48     for(int i=0; i<ans; i++) {
    49         left+=area[i];
    50     }
    51     for(int i=ans; i<R; i++) {
    52         right+=area[i];
    53     }
    54     while(area[ans]==0&&ans+1<=R) ans++; 
    55     printf("%d",ans);
    56     return 0;
    57 }

    备注:首先是要读懂题!然后看到最值问题想到二分!!

    【使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小】这是首先要满足的条件,【使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小】是其次要满足的。

    我特别愚蠢的不明白为啥在满足第一个条件的前提下还有第二个条件呢,因为分割线可能落在没有任何小矩形的空白区域,在这种情况下就要把分割线尽可能往右挪,只要不改变左右面积之差就行。

    【如果必要的话,x=R也可以是答案。】这句话在我的代码里其实不用特判,这是针对右边根本没有矩形的情况,那么就跟前面一样把分割线一直往右挪,到右边界就行了。也就是标黄的那行代码全部都考虑进去了。

    二分的时候也有很多问题。首先是area这个数组的预处理,我是开了一个R大的数组,按照每一个单位长度这么处理,但也可以光存矩形(https://www.cnblogs.com/Shallow-dream/p/11721641.html),这样速度会有所提升,在这种情况下就要特判x=R那种情况了好像,我也不太清楚……。

    按照我的习惯,check过的mid存在ans里,解决不知道最终结果是l,r还是mid的问题。

    和其它题不一样的是,check返回值是个整型。我突然发现也可以不用这样,没必要把left==right这种情况单独提出来,完全可以直接>=就return true,也可以AC。

    要注意不能总是check,能不直接调用check就不要用check,比如标黄那行,如果用check来判断就肯定会超时。 

    temp<0是说明mid是不可以的,这时候l=mid+1就行,而不是l=mid。

  • 相关阅读:
    其实天很蓝,阴云终要散;其实海不宽,此岸连彼岸;其实泪也甜,当你心如愿。
    三大杀手,是不是杀到了你的内心深处呢?
    “天才就是1%的灵感加上99%的汗水”还有后半句
    oracle中的exists in 和not exists 用法详解
    toString()方法的作用
    oracle学习笔记——视图、索引(转载)
    serialVersionUID的作用
    pageEncoding和charset有什么区别
    equal(),hashcode(),toString()方法的作用
    库函数&linux下的系统调用 对比
  • 原文地址:https://www.cnblogs.com/fangziyuan/p/13118000.html
Copyright © 2020-2023  润新知