题目
平面上有一个大矩形,其左下角坐标 (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
题意分析
这道题首先给出一个大矩形,在大矩形中有许多小矩形,大矩形的宽为R,要在(0,R)内找出一条分割线,保证在分割线左侧所有小矩形的面积和大于等于右侧所有小矩形的面积和,在分割线之间的矩形也算,还要保证左边所有小矩形面积和与右侧所有小矩形面积和之差最小,即(S左-S右)最小!最后在保证(S左-S右)最小的情况下,求得分割线横坐标x尽量大!
解题思路
首先暴力想法,从0开始枚举x,把所有满足题意的x记录下来,最后求最大x!
暴力思想可以满足小数据,但是R为10 ^ 6显然不可能暴力通过!
怎么办呢?
既然线性搜索不满足,那么想当然二分搜索啊!
我们先找一下这道题的单调关系
首先可以看到,随着x增大左侧所有小矩形面积也会增大,右侧所有小矩形面积会减小,所以S左-S右会随着x增大而增大!
有了单调关系我们套入二分答案的模板就可以,那么if()中的check函数怎么写呢?
我们需要求出左侧所有小矩形面积和,然后和所有小矩形面积和进行比较,
如果S左*2>=S总,证明x取大了,right=mid 如果小于x取小了,需要left=mid!
怎么做到在满足S左-S右最小,并且尽量使x大呢?
我们先看一副图片
从图片中我们可以看到,第一条分割线显然不是最优的,第二条分割线才是最优的!
我们只需要让x++,并且满足S左-S右的差值不变就可以,看x究竟能加到多少,就是要求的x最大值!
完整代码
#include<cstdio>//uncle-lu
#include<algorithm>
#include<iostream>
using namespace std;
struct node{
/*
* x : x坐标
* y : y坐标
* h : 高度
* w : 宽度
*/
long long int x,y,h,w;
}a[10010];
long long int tot;
long long int r,n;
/* sum函数
* 作用:
* 统计分割线左边的面积和
* 变量:
* x : 分割线
* sum_all : 分割线左边所有矩阵的和
*/
long long int sum(long long int x)
{
long long int sum_all=0;
for(int i=1;i<=n;++i)
{
if(a[i].x+a[i].w<=x)
{
sum_all+=a[i].h*a[i].w;
}
else if(a[i].x<x&&a[i].x+a[i].w>x)
{
sum_all+=a[i].h*(x-a[i].x);
}
else break;
}
return sum_all;
}
int main()
{
scanf("%lld",&r);
scanf("%lld",&n);
for(int i=1;i<=n;++i)
{
scanf("%lld%lld%lld%lld",&a[i].x,&a[i].y,&a[i].w,&a[i].h);
tot+=(a[i].h*a[i].w);
}
/*
* ans_mean : 记录答案分割线分割出的左区间大小
* ans : 记录答案分割线
*/
long long int left=0,right=r+1,mid;
long long int ans_mean;
long long int ans=0;
while(left+1<right)
{
mid=(left+right)/2;
long long int sum_left=sum(mid);
if(sum_left*2>=tot)
{
ans_mean=sum_left;
ans=mid;
right=mid;
}
else left=mid;
}
/*
* while将分割线尽量往右移(处理存在空白空挡的部分)
*/
while(ans<r&&sum(ans+1)==ans_mean)ans++;
printf("%lld\n",ans);
return 0;
}
如果对你有帮助,多多点赞支持,感谢观看!