題目鏈接: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;