题目链接:http://acm.uva.es/p/v1/109.html
一道综合性的几何题,题目看上去比较难,因而提交量也较其它题目少.
题目大意如下:
在500×500大小的虚拟空间中,存在N个王国,每个王国由一个电站和M个居民组成.王国的范围是一个包含其全部居民和电站的最小凸多边形.
然后给出至少一个导弹着陆的位置,凡是导弹着陆点位于某个王国的范围,则这个王国的电站被破坏,不杀伤居民.
求最后剩下的电站被破坏的王国的总面积.
输入一个整数M,接下来有M组数据,第一个是该王国电站的x,y坐标(坐标均为整数,范围[0,500]).然后是M-1组居民坐标.
有若干个王国的数据,重复以上,-1代表结束输入.然后有若干个导弹的坐标(x,y).
王国数量不超过20,每个王国的坐标(居民数加一个电站)不超过100,导弹数不超过100.
输出只有一个,总面积为浮点数,保留两位小数,(事实上,结果小数位一定为x.50或x.00)
提供一个求多边形面积的公式:这里(x1,y1)~(xn,yn)为多边形顶点坐标,x0=xn,y0=yn
如果多边形顶点顺序为逆时针,则结果为正,反之结果为负.
即a=abs(x0y1-x1y0+x1y2-x2y1+......+xn-1yn-xnyn-1)/2
这个公式是由,求多边形向量的叉积演变过来的.详细推导过程
既然求多边形面积有公式可参考,那么题目的难点就在于:
已知若干个点,求能够包含全部点的凸多边形.(一定为凸多边形,否则你试试怎么确定唯一)
这里还有一个公式:对于向量(x1,y1)->(x2,y2),判断点(x3,y3)在向量的左边,右边,还是线上.
p=x1(y3-y2)+x2(y1-y3)+x3(y2-y1).
p<0 左侧
p=0 线上
p>0 右侧
然后,卷包裹大法,^_^
找到最外面的一个点,比如最底下(y最小)的一个点.
然后比较各点,找出具有最大夹角的那个点,最后回到出发点.
注意特殊情况,三点共线.
当然还有很多求凸包的算法,详见<算法艺术与信息学竞赛>一书.[p391]
附上代码: 源代码下载
/* *********************************** */ 2
/* Pro_109 SCUD Busters */ 3
/* CPU Time 0:00.002 Memory Minimum */ 4
/* Ranklist 147 Programmed By Wingy */ 5
/* *********************************** */ 6
7
#include < stdio.h > 8
#define MAX_KINGDOMS 20 // 最大王国数量 9
#define MAX_INDICATES 100 // 单个王国最多地点数 10
#define MAX_SCUD 100 // 最多导弹数 11
12
int indicates_x[MAX_KINGDOMS * MAX_INDICATES + MAX_SCUD + 1 ] = { - 1 } ; // 地点x坐标 13
int indicates_y[MAX_KINGDOMS * MAX_INDICATES + MAX_SCUD + 1 ] = { - 1 } ; // 地点y坐标14
// 所有地点信息集成存放,总数量为王国数×每个王国地点数+导弹数15
// 这样的好处是,以后函数调用唯一id信息即可. 16
int relation( int id1, int id2, int id3) // 点与直线关系函数 17
{ // id1->id2为向量,id3为待判断的点 18
int temp = indicates_x[id1] * (indicates_y[id3] - indicates_y[id2])19
+ indicates_x[id2] * (indicates_y[id1] - indicates_y[id3])20
+ indicates_x[id3] * (indicates_y[id2] - indicates_y[id1]);21
if (temp == 0 ) // 点在直线上的特殊情况集成处理 22
{23
if (indicates_x[id2] == indicates_x[id1])24
{25
if (indicates_y[id1] > indicates_y[id2])26
temp = indicates_y[id3] - indicates_y[id2];27
else temp = indicates_y[id2] - indicates_y[id3];28
} 29
else 30
{31
if (indicates_x[id1] > indicates_x[id2])32
temp = indicates_x[id3] - indicates_x[id2];33
else temp = indicates_x[id2] - indicates_x[id3];34
} 35
} 36
return temp;37
} 38
int kingdomarea( int id1, int id2) // 王国面积累加函数 39
{40
return indicates_x[id2] * indicates_y[id1]41
- indicates_x[id1] * indicates_y[id2];42
} 43
44
int main()45
{46
int kingdom_area = 0 , i, j, k, max_id, start_id, top_id;47
// kingdom_area 王国面积, start_id为向量起点,max_id为向量终点,top_id卷包裹的起点 48
int surrounds_number, indicates_id = 1 , kingdom_number = 0 ;49
// surrounds_number王国凸包顶点数,kingdom_number王国数.indicates_id地点id计数器 50
int SCUD_idstart = MAX_KINGDOMS * MAX_INDICATES + 1 , SCUD_idend = SCUD_idstart;51
// SCUD_idstart导弹信息起始id , SCUD_idend 导弹信息结束id 52
int kingdom[MAX_KINGDOMS][MAX_INDICATES]; // 记录凸包(王国)向量序列. 53
int kingdom_surrounds_number[MAX_KINGDOMS + 1 ]; // 记录凸包(王国)顶点数 54
int kingdom_startid[MAX_KINGDOMS], kingdom_endid[MAX_KINGDOMS];55
// 记录某个王国内地点的起始id和终止id 56
int bool_indicates[MAX_KINGDOMS * MAX_INDICATES + 1 ] = { 0 } ;57
// 标记地点是否已作为凸包顶点. 58
59
while ( 1 ) // 王国信息输入 60
{61
scanf( " %d " , & k);62
if (k == - 1 ) break ;63
kingdom_startid[kingdom_number] = indicates_id;64
kingdom_endid[kingdom_number] = indicates_id + k;65
top_id = 0 ;66
for (i = 0 ; i < k; i ++ )67
{68
scanf( " %d%d " , & indicates_x[indicates_id], & indicates_y[indicates_id]);69
if (indicates_y[indicates_id] > indicates_y[top_id]70
|| (indicates_y[indicates_id] == indicates_y[top_id]71
&& indicates_x[indicates_id] > indicates_x[top_id]))72
top_id = indicates_id; // 找出每个王国卷包裹的起始地点 73
indicates_id ++ ;74
} 75
kingdom[kingdom_number][ 0 ] = top_id;76
kingdom_number ++ ;77
} 78
79
for (i = 0 ; i < kingdom_number; i ++ ) // 处理单个王国的凸包信息 80
{81
start_id = kingdom[i][ 0 ];82
surrounds_number = 0 ;83
do {84
max_id = kingdom_startid[i] + (kingdom_startid[i] == start_id);85
for (j = kingdom_startid[i]; j < kingdom_endid[i]; j ++ )86
{87
if (bool_indicates[j] == 1 || j == max_id || j == start_id) continue ;88
if (relation(start_id, max_id,j) < 0 ) max_id = j;89
} 90
bool_indicates[max_id] = 1 ;91
kingdom[i][ ++ surrounds_number] = max_id;92
start_id = max_id;93
} while (start_id != kingdom[i][ 0 ]);94
kingdom_surrounds_number[i] = surrounds_number;95
} 96
97
while (scanf( " %d%d " , & indicates_x[SCUD_idend], & indicates_y[SCUD_idend]) != EOF)98
SCUD_idend ++ ;99
// 输入导弹信息 100
101
for (i = 0 ; i < kingdom_number; i ++ ) // 处理导弹攻击 102
{103
for (k = SCUD_idstart; k < SCUD_idend; k ++ )104
{105
for (j = 0 ; j < kingdom_surrounds_number[i]; j ++ )106
if (relation(kingdom[i][j], kingdom[i][j + 1 ], k) < 0 ) break ;107
if (j == kingdom_surrounds_number[i]) break ;108
} 109
if (k == SCUD_idend) continue ;110
for (j = 0 ; j < kingdom_surrounds_number[i]; j ++ )111
kingdom_area += kingdomarea(kingdom[i][j], kingdom[i][j + 1 ]);112
} 113
114
printf( " %.2f\n " , kingdom_area / 2.0 );115
return 0 ;116
}
浙公网安备 33010602011771号