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,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);
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){
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;
}