郁金香编程俱乐部

It's a bundle of crazy programmers, we discuss topics like data structure, algorithm, STL, AI,
Graph Theory, C++, Java, anything you can think of while programming...    
The club was founded in the year 2005, Ningbo, China.
posts - 3, comments - 1, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

UVA_Pro_109

Posted on 2006-12-07 14:42 TPC2005 阅读(512) 评论(1)  编辑 收藏 引用 所属分类: UVA 题目讨论

题目链接: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]
附上代码:
源代码下载

  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 }


 

Feedback

# re: UVA_Pro_109  回复  更多评论   

2006-12-07 18:18 by TPC2005
下定决心把不良的编程习惯给改了,用上了长变量名,
不过为什么还有效率低下的感觉,不习惯。
以前写程序,就那么几个变量,abcd随便定义,心里清楚代表什么,
现在老长的变量名,反而需要反映半天才明白代表什么,,,,
而且,总觉得变量名加长,分模块,程序跑起来会慢。。。。
程序可读性增加,同时也要牺牲效率的吧。^_^


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理