题目链接: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
![](http://acm.uva.es/p/v1/109img5.gif)
如果多边形顶点顺序为逆时针,则结果为正,反之结果为负.
即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 右侧
然后,卷包裹大法,^_^
![](//www.cppblog.com/images/cppblog_com/tpc2005/3186/o_%e6%9c%aa%e5%91%bd%e5%90%8d.jpg)
找到最外面的一个点,比如最底下(y最小)的一个点.
然后比较各点,找出具有最大夹角的那个点,最后回到出发点.
注意特殊情况,三点共线.
当然还有很多求凸包的算法,详见<算法艺术与信息学竞赛>一书.[p391]
附上代码: 源代码下载
1
/**/ /* *********************************** */
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
}