题目链接: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 }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步