矩形面积
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 168 Accepted Submission(s): 93
Problem Description
小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。
Input
第一行一个正整数 T,代表测试数据组数(1≤T),接下来 T 组测试数据。
每组测试数据占若干行,第一行一个正整数 N,代表矩形的数量。接下来 N 行,每行 8 个整数x1,y,代表矩形的四个点坐标,坐标绝对值不会超过10000。
每组测试数据占若干行,第一行一个正整数 N,代表矩形的数量。接下来 N 行,每行 8 个整数x1,y,代表矩形的四个点坐标,坐标绝对值不会超过10000。
Output
对于每组测试数据,输出两行:
第一行输出"Case #i:",i 代表第 i 组测试数据。
第二行包含1 个数字,代表面积最小的矩形的面积,结果保留到整数位。
第一行输出"Case #i:",i 代表第 i 组测试数据。
第二行包含1 个数字,代表面积最小的矩形的面积,结果保留到整数位。
Sample Input
2
2
5 10 5 8 3 10 3 8
8 8 8 6 7 8 7 6
1
0 0 2 2 2 0 0 2
Sample Output
Case #1:
17
Case #2:
4
Source
1 #include<stdio.h> 2 #include<set> 3 #include<algorithm> 4 #include<string.h> 5 #define one first 6 #define two second 7 #define zero 1e-12 8 #define sgn(x) (fabs(x)<zero?0:(x>0?1:-1)) 9 #define shadow(a,b,c) ((b.one-a.one)*(c.one-a.one)+(b.two-a.two)*(c.two-a.two))/sqrt((b.one-a.one)*(b.one-a.one)+(b.two-a.two)*(b.two-a.two)) 10 #define cross(a,b,c) ((b.one-a.one)*(c.two-a.two)-(b.two-a.two)*(c.one-a.one)) 11 #define cmp(a,b) (a.one<b.one || sgn(a.one-b.one)==0 && a.two<b.two) 12 const int M = 5000 , inf = 0x3f3f3f3f ; 13 int n ; 14 std::pair<double , double > p[M] ; 15 double s[M]; 16 std::pair<double , double > jk[M] ; 17 int tot ; 18 19 void hull(int l,int r, std::pair<double , double>a,std::pair<double , double> b){ 20 int x=l,i=l-1,j=r+1,k; 21 for (k=l;k<=r;k++){ 22 double temp=sgn(s[x]-s[k]); 23 if (temp<0 || temp==0 && cmp(p[x],p[k])) x=k; 24 } 25 std::pair<double , double> y=p[x]; 26 for (k=l;k<=r;k++){ 27 s[++i]=cross(p[k],a,y); 28 if (sgn(s[i])>0) std::swap(p[i],p[k]); else i--; 29 } 30 for (k=r;k>=l;k--){ 31 s[--j]=cross(p[k],y,b); 32 if (sgn(s[j])>0) std::swap(p[j],p[k]); else j++; 33 } 34 if (l<=i) hull(l,i,a,y); 35 jk[tot ++] = y ; 36 if (j<=r) hull(j,r,y,b); 37 } 38 39 void solve () 40 { 41 double l , r , h , sum = inf ; 42 double d = 0 ; 43 for (int i = 1 ; i <= tot ; i ++) { 44 d = (jk[i].one - jk[i - 1].one) * (jk[i].one - jk[i - 1].one) + (jk[i].two - jk[i - 1].two) * (jk[i].two - jk[i - 1].two) ; 45 l = 0 , r = sqrt (d) ; 46 h = 0 ; 47 for (int j = 1 ; j <= n ; j ++) { 48 if (p[j] != jk[i] && p[j] != jk[i - 1]) { 49 h = std::max (h , fabs (cross (jk[i] , jk[i - 1] , p[j])) / sqrt(d)) ; 50 l = std::min (l , shadow (jk[i] , jk[i - 1] , p[j])) ; 51 r = std::max (r , shadow (jk[i] , jk[i - 1] , p[j])) ; 52 } 53 } 54 sum = std::min (sum , (r - l) * h) ; 55 } 56 printf ("%.0f " , sum) ; 57 } 58 59 int main(){ 60 //freopen ("a.txt" , "r" , stdin) ; 61 int T , cas = 1 ; 62 scanf ("%d" , &T) ; 63 while (T --) { 64 printf ("Case #%d: " , cas ++) ; 65 memset (s , 0 , sizeof(s)) ; 66 int i , x=0 ; 67 tot = 0 ; 68 scanf("%d",&n); 69 n *= 4 ; 70 for (i=1;i<=n;i++){ 71 scanf("%lf%lf",&p[i].one,&p[i].two); 72 if (x==0 || cmp(p[i],p[x])) x=i; 73 } 74 std::swap(p[1],p[x]); 75 jk[tot ++] = p[1] ; 76 hull(2,n,p[1],p[1]); 77 jk[tot] = jk[0] ; 78 solve () ; 79 } 80 return 0; 81 }
用这种做法,首先要明白最小的覆盖矩形的其中一边必定是和壳重合。
所以我们只要求出壳,并枚举壳上的每条边,并计算出在它上生成的最小矩阵面积。然后找出其中最小的就好了。
复杂度((4*n)^2);
1 #include<bits/stdc++.h> 2 #define one first 3 #define two second 4 #define eps 1e-12 5 #define sgn(x) (x<eps?0:(x>0?1:-1)) 6 #define cross(a,b,c) ((b.one-a.one)*(c.two-a.two)-(b.two-a.two)*(c.one-a.one)) 7 #define cmp(a,b) (a.one<b.one || sgn(a.one-b.one)==0 && a.two<b.two) 8 const int M = 1e5 , inf = 0x3f3f3f3f ; 9 int n ; 10 std::pair<double , double> p [M] ; 11 int s[M] ; 12 std::pair<double , double> jk[M] ; 13 int tot = 0 ; 14 15 void hull (int l , int r , std::pair<double , double> a , std::pair<double , double> b) 16 { 17 //printf ("(%d , %d) " , l , r ) ; 18 int x = l , _l = l - 1 , _r = r + 1 ; 19 for (int i = l ; i <= r ; i ++) { 20 int tmp = sgn(s[i]-s[x]) ; 21 if (tmp > 0 || tmp == 0 && cmp(p[x],p[i])) x = i ; 22 } 23 for (int i = l ; i <= r ; i ++) { 24 s[++ _l] = cross(p[i],a,p[x]) ; 25 if (sgn(s[_l]) > 0) std::swap (p[i] , p[_l]) ; else _l -- ; 26 } 27 for (int i = r ; i >= l ; i --) { 28 s[-- _r] = cross(p[i],p[x],b) ; 29 if (sgn(s[_r]) > 0) std::swap (p[i] , p[_r]) ; else _r ++ ; 30 } 31 if (l <= _l)hull (l , _l , a , p[x]) ; 32 jk[tot ++] = p[x] ; 33 if (_r <= r)hull (_r , r , p[x] , b) ; 34 } 35 36 int main () 37 { 38 // freopen ("a.txt" , "r" , stdin ) ; 39 memset (s , 0 , sizeof(s)) ; 40 scanf ("%d" , &n) ; //printf ("n = %d " , n ) ; 41 int x = -1 ; 42 for (int i = 1 ; i <= n ; i ++) { 43 scanf ("%lf%lf" , &p[i].one , &p[i].two) ; 44 if (x == -1 || cmp (p[i] , p[x])) x = i ; 45 } 46 std::swap (p[x] , p[1]) ; 47 jk[tot ++] = p[1] ; 48 hull (2 , n , p[1] , p[1]) ; 49 for (int i = 0 ; i < tot ; i ++) printf ("(%.4f , %.4f) " , jk[i].one , jk[i].two) ; 50 return 0 ; 51 }
要了解这个算法的思路,先去维基上看看动态演示http://en.wikipedia.org/wiki/Quickhull
然后你需要知道叉积(叉积 "X" 表示):已知两个向量 a = (x1 , y1) , b = (x2 , y2);
叉积:a X b = x1 * y2 - x2 * y1 ;
点积:a * b = x1 * x2 + y1 * y2 ;
两个向量间的的夹角:a * b = |a| * |b| * cosθ ;
那么这两个向量 构成的三角形面积 S = 1/2 * abs (a X b) ,
还有 a X b = - b X a ;
知道以上这两个概念,就看的动quickhull了。
就这道题而言,你还需要知道, a 在 b 上的投影求法:a * b / | b | ;