03:矩形分割
2021/10/25 23:10:39
本文主要是介绍03:矩形分割,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
03:矩形分割
理解题目还挺重要的 ,不然一直在那儿白费脑力
描述
平面上有一个大矩形,其左下角坐标(0,0),右上角坐标(R,R)。大矩形内部包含一些小矩形,小矩形都平行于坐标轴且互不重叠。所有矩形的顶点都是整点。要求画一根平行于y轴的直线x=k(k是整数) ,使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。并且,要使得大矩形在直线左边的的面积尽可能大。注意:若直线穿过一个小矩形,将会把它切成两个部分,分属左右两侧。
一开始看题,既要左右面积差值最小又要大矩形左侧面积最大,像极了数学里高深的优化问题,难不成还得来个多元线性回归?!
不重叠,说明 要求大矩形左侧面积尽可能大,只是建立在 已经找到小矩形们左右面积差最小的位置 这一条件之上,如果找到的位置x可以向右移动并且不改变左右侧小巨星们的面积(怎么才能实现这一条件呢?自然是这个位置位于两个小矩形之间的留白处,那么就将x进一步向右调整到下一个小矩形的左侧,跨过整个留白区, 或者是跨国最后一个小矩形与大矩形右边界的留白区),这种操作可以视作两种情况,也可合二为一
while(areaD(x)==areaD(x+1)&&x<a){ x++; }//x可以取到大矩形右边界a cout<<x;
#include <bits/stdc++.h> using namespace std; typedef long long int ll; ll a;//大正方形右上角坐标 ll n;//n个小矩形 //int lx,ly,w,h;//每个小矩形左上角坐标和宽、高 // int a[MAX][4]; struct matrix{ ll l,u,w,h;//左上宽高 }m[10001]; ll Size=0;//总面积 ll areaD(int x){ ll leftS=0; for(int i=0;i<n;i++){ if(m[i].l<x){ if(m[i].l+m[i].w<=x)leftS+=m[i].w*m[i].h; else leftS+=(x-m[i].l)*m[i].h; } } ll differ=leftS-(Size-leftS); return differ; } ll search(int left,int right){ ll mid; while(left<=right){ mid=left+(right-left)/2; if(areaD(mid)>0)right=mid-1; else if(areaD(mid)<0)left=mid+1; else return mid; } return left; } int main(int argc, char** argv) { cin>>a>>n; for(int i=0;i<n;i++){ cin>>m[i].l>>m[i].u>>m[i].w>>m[i].h; Size+=m[i].w*m[i].h; } ll x=search(0,a); while(areaD(x)==areaD(x+1)&&x<a){ x++; }//x可以取到大矩形右边界a cout<<x; return 0; }
总而言之:
1、能找到面积相等的位置,就继续探寻是否能跨过一个留白区,找到满足“左侧面积 = =右侧面积”的最大整数值
2、对于右侧面积为0的:最终值 = 大矩形的边界 r
3、若不属于以上两种情况,就通过while(left<=right)这种二分寻找跳出循环后的left,就是使左右侧面积最接近的位置
还有一个迷惑问题,search函数中将else if改成if,结果惊人1000???
接下来关注到二分查找的细节
二分查找的截止判断条件
-
while(left<=right)……
-
while(left<right)……
-
while(left<right-1)……
找到了mid倒无甚区别,,如果没找到还需要依赖left与right的final值来确定与所求值最接近的数据的一个范围
NO.3:
跳出循环后,left == right-1,left与right紧紧相邻
需要二分枚举的答案是整数,所以可以用这个方式结束,
像这题,还需要分别判断left 和 right哪个更能使左右小矩形们面积之差靠近
并且在循环体内部
if (sum>0) r=mid;
else if (sum<=0) l=mid;
else if (sum==0) {printf("%d",mid);return 0;}
左右边界值的修改是这样进行的
NO.1
跳出循环后
在循环内部这样修改边界值
while(left<=right){
mid=left+(right-left)/2;
if(areaD(mid)>0)right=mid-1;
else if(areaD(mid)<0)left=mid+1;
else return mid;
}
return left;NO.2
while(left<right)……
在循环内部这样修改边界值
if(leftS<=rightS) //为了尽量让k靠右,需要用小于等于
{
l=mid;
}
if(leftS>rightS) //为了尽量减少面积差
{
r=mid;
}在循环体内部,可以进行最后一次二分进行判断
if(r-l==1) //经过二分后的最后判断NO.1、2都默认跳出循环之后/最后一次循环中,left是最接近寻找的值
如果mid可能包括要找的值和其他,那么修改边界值(目的是继续寻找边界,边界就要取上一次的mid
while(l<r){/*lower_pound()二分求下界*/ mid=l+(r-l)/2; if(2*sa[mid]>=sa[R])r=mid;//必须如此,不可r=mid-1;亲测错误 else l=mid+1; } return l; }
NO.1、2都默认跳出循环之后/最后一次循环中,left是最接近寻找的值 也就是要寻找的值可能会出现在边界值里 如果将NO.1的mid+/-1改成mid。将NO.2的mid+1改成mid,都会超时 好的,乱了 关于修改边值条件,到底是mid+/-1 还是 mid, 要回到二分法最初的思想,就是在target出现的更小的范围内继续二分,也就是说,接下来划定的范围无论如何一定要包括target 但是已经排除的范围不能再加到下一次二分的范围。 这题target是areaD(mid)==0,或者是areaD(mid)>0的范围里取一个 最接近mid的 一、 每次二分都是奔着mid=0去的,因此mid不满足条件,就不用再加入下次二分的范围内 最后一次二分,left==right=mid,mid还不满足条件,那必定是mid-1小于0,mid+1大于0,根据题意我们选的是后者 这种二分方式,循环结束后left是比所求得target稍大得那个 二、 ,mid小于等于0包含了mid==0,mid也许就是最后的target,故需要加入下次二分的范围内。 每次二分其实是为了找到mid>0得最小mid去的,因此mid>0符合所求,故加入下一次二分范围。 不同于NO3的是跳出循环后,mygod 三、 每次二分其实是为了找到mid>0得最小mid去的,因此mid>0符合所求,故加入下一次二分范围,mid小于等于0包含了mid==0,mid也许就是最后的target,故需要加入下次二分的范围内 跳出循环后,left<right并相邻,并不知道谁更靠近target所以要判断比较
这篇关于03:矩形分割的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-01Java部署教程:新手入门指南
- 2024-11-01Java部署教程:从入门到实践
- 2024-11-01Java订单系统教程:新手入门指南
- 2024-11-01Java分布式教程:新手入门指南
- 2024-11-01Java管理系统教程:新手入门详解
- 2024-11-01Java监控系统教程:从入门到实践
- 2024-11-01SpringCloud Alibaba入门:轻松搭建微服务架构
- 2024-11-01Swagger入门:新手必读指南
- 2024-11-01Swagger入门:轻松搭建API文档
- 2024-11-01uni-APP入门:新手快速上手指南