矩形分割


題目鏈接:http://noi.openjudge.cn/ch0111/03/

一個二分的題目,估計是數據類型選擇不當,折騰了好多天。所以,以后記得盡管使用long  long類型數據呵呵

 

描述

平面上有一個大矩形,其左下角坐標(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

我的思路:對解區間[0,R]做二分,查找可能的解。(假設每一次二分計算的中間點是解,然后做驗證。然后在尋找更合理的解。)
注意:long long類型數據要使用%lld輸入輸出。
暫時還有一個疑惑,就是末尾的絕對值部分。(這個下面再提)
AC代碼:
 1 #include<stdio.h>
 2 #include<math.h>
 3 struct obj
 4 {
 5     long long left,top,w,h;//左上角橫、縱坐標和寬、高值
 6     long long rx;//右下角頂點的橫坐標 
 7     long long s;//面積 
 8 };
 9 long long sigema(struct obj a[],int n,int mid)
10 {
11     int i;
12     long long sum1,sum2;
13     sum1=sum2=0;
14     for(i=0;i<n;i++)
15     {
16         if(a[i].rx<=mid) sum1+=a[i].s; //該矩形屬左側
17         else if(a[i].left>=mid) sum2+=a[i].s;//屬右側
18         else
19         {
20             sum1+=a[i].h*(mid-a[i].left);
21             sum2+=a[i].h*(a[i].rx-mid);
22         }
23     }
24     return sum1-sum2;
25 }
26 int main()
27 {
28     long long r,n,i;
29     struct obj a[10005];
30     long long minx,maxx,mid,ans,maxx2;
31     long long temp,temp1,temp2;
32     
33     scanf("%lld%lld",&r,&n);
34     for(i=0;i<n;i++) 
35     {
36         scanf("%lld%lld%lld%lld",&a[i].left,&a[i].top,&a[i].w,&a[i].h);
37         a[i].s=a[i].w*a[i].h;
38         a[i].rx=a[i].left+a[i].w;
39         if(i==0)  maxx2=a[i].rx;
40         else
41         {
42             if(a[i].rx>maxx2) maxx2=a[i].rx;
43         }
44     }
45     minx=0;
46     maxx=r;
47     while(minx+1<maxx)//需要二分枚舉的答案是整數,所以可以用這個方式結束 
48     {
49         mid=(minx+maxx)/2;
50         temp=sigema(a,n,mid);
51         if(temp>0) maxx=mid;
52         else if(temp<=0) minx=mid;
53     }
54     
55     temp1=sigema(a,n,minx);
56     temp2=sigema(a,n,maxx);
57     /*if(fabs(temp1)<fabs(temp2)) ans=minx;
58     else ans=maxx;*/
59     if( temp1<temp2)
60     {
61         if(temp1>=0) ans=minx;
62         else ans=maxx;
63     }
64     else if(temp1>temp2)
65     {
66         if(temp2>=0) ans=maxx;
67         else ans=minx;
68     }
69     else ans=maxx;
70     if(ans==maxx2) ans=r;
71     printf("%lld\n",ans);
72     return 0;
73 }

這個題目一直有一個疑惑。最開始的時候題目並未有提到有一個大矩形,題目的要求僅僅是“左邊部分小矩形的面積之和不小於右邊部分小矩形面積之和”。后來題目做了修改,增加了大矩形的約束。參看了別人的代碼,也自己驗證過,代碼后面的if語句里面的絕對值竟然在修改題目后還可以通過。就是下面的語句:

if(fabs(temp1)<fabs(temp2)) ans=minx;
else ans=maxx;

  我一直都覺得這個語句只能起到約束題目的第一個條件(小矩形落在直線左邊的面積必須大於等於落在右邊的面積,且兩邊面積之差最小),並不能夠約束題目的第二個條件(要使得大矩形在直線左邊的的面積盡可能大)。因為若是直接取絕對值,不能夠保證哪一邊的面積比較大,只是能夠保證兩邊的面積差距盡量小。

所以,我在后面代碼里面做了修改,用的是這樣的代碼:
 1 if( temp1<temp2)
 2     {
 3         if(temp1>=0) ans=minx;
 4         else ans=maxx;
 5     }
 6     else if(temp1>temp2)
 7     {
 8         if(temp2>=0) ans=maxx;
 9         else ans=minx;
10     }
11     else ans=maxx;

 

另外,還要一個地方比較重要,那就是最后一個if語句。

1 if(ans==maxx2) ans=r;




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM