http://acm.hdu.edu.cn/showproblem.php?pid=3952
这是一道计算几何的题目,刚好昨晚看了一下基本的计算几何,而这题就是叉积的应用。
设有两个共起点向量 A,B, 其中 A = { x1, y1 }, B = { x2, y2 }; A X B = x1 * y2 + x2 * y1;
如果 A X B > 0, 那么说明A向量在B向量的顺时针方向,反之则说明A在B的逆时针方向。等于零,说明两个向量重合。
那么对于某一点,就可以运用这一点来判定该点是在线段的左还是右了,即选择一个端点,再连接这个端点和所求的一点,构造出另一个向量......
这题是给定了N个水果的各个端点,那么我们可以断定这个穿过最多的水果的直线如果不全由端点构成的话,一定会有无数条,因为稍微的旋转和平移这条直线一定还是满足最多的水果的。因此,该题就转化为枚举所有的两点确定的一条之直线,然后计算哪条直线穿过的水果数最多。
代码如下:
#include <cstdio> #include <cstring> #include <cstdlib> using namespace std; struct fruit { int num; int p[12][2]; }f[12]; int point[105][2]; int dir( int p1, int p2, int x0, int y0 ) // 计算叉积 { return ( x0 - point[p1][0] ) * ( point[p2][1] - point[p1][1] ) + ( y0 - point[p1][1] ) * ( point[p2][0] - point[p1][0] ); } bool attack( int p1, int p2, int No ) { for( int i = 0; i < f[No].num; ++i ) { for( int j = i + 1; j < f[No].num; ++j ) { if( dir( p1, p2, f[No].p[i][0], f[No].p[i][1] ) * dir( p1, p2, f[No].p[j][0], f[No].p[j][1] ) <= 0 ) return true; }// 由于是凸多边形才可以这样做 } return false; } int cal( int p1, int p2, int N ) { int ans = 0; for( int i = 0; i < N; ++i ) { if( attack( p1, p2, i ) ) ans++; } return ans; } int main( ) { int T; scanf( "%d", &T ); for( int t = 1; t <= T;\ ++t ) { int N, sum = 0, max = 0; scanf( "%d", &N ); for( int i = 0, j = 0; i < N; ++i ) { int K; scanf( "%d", &K ); f[i].num = K; sum += K; for( int t = 0; t < K; ++j, ++t ) { scanf( "%d %d", &point[j][0], &point[j][1] ); f[i].p[t][0] = point[j][0]; f[i].p[t][1] = point[j][1]; } } for( int i = 0; i < sum; ++i ) { for( int j = i + 1; j < sum; ++j ) { int temp = cal( i, j, N ); max = max > temp ? max : temp; } } printf( "Case %d: %d\n", t, max ); } }