NYACM_003

来源:互联网 发布:汉王文本王扫描仪软件 编辑:程序博客网 时间:2024/06/06 03:01

题目:多边形重心问题

链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=3

描述:
在某个多边形上,取n个点,这n个点顺序给出,按照给出顺序将相邻的点用直线连接, (第一个和最后一个连接),所有线段不和其他线段相交,但是可以重合,可得到一个多边形或一条线段或一个多边形和一个线段的连接后的图形;
如果是一条线段,我们定义面积为0,重心坐标为(0,0).现在求给出的点集组成的图形的面积和重心横纵坐标的和;
输入
第一行有一个整数0<n<11,表示有n组数据;
每组数据第一行有一个整数m<10000,表示有这个多边形有m个顶点;
输出
输出每个多边形的面积、重心横纵坐标的和,小数点后保留三位;


分析:只要知道怎么求多边形重心,编程没什么难度,第一次看到这个问题的时候确实不知道如何求解多边形重心,正好扩展眼界,这也是从零开始不论难度过一遍的原因之一。

搜到一片博客(http://blog.csdn.net/mnlghttr/article/details/17056831),正好也是对这一题的求解,看一下做法就好,自己编码实现。


AC code:


#include <iostream>
#include <vector>
#include <iomanip>
#include <stdlib.h>
#include <math.h>

using namespace std;

typedef struct Point_
{
double x;
double y;
Point_(double tx, double ty)
{
x = tx;
y = ty;
}
}Point;  //存储点坐标

Point GetTriangleBarycentre(Point p1, Point p2, Point p3)
{
Point ret(0, 0);
ret.x = (p1.x + p2.x + p3.x) / 3;
ret.y = (p1.y + p2.y + p3.y) / 3;
return ret;
}
double GetTriangleArea(Point p1, Point p2, Point p3)
{
double area = 0;
area = ((p2.x - p1.x)*(p3.y - p1.y) - (p3.x - p1.x)*(p2.y - p1.y)) / 2;
return area;
}
double GetTotalArea(vector<double> areas)
{
double ret = 0.0;
for (int i = 0; i < areas.size(); i++)
{
ret += areas[i];
}
return ret;
}
Point GetLastBarycentrePoint(vector<double> areas, vector<Point> points)
{
Point ret(0, 0);
double totalarea = GetTotalArea(areas);
if (fabs(totalarea) < 1e-7)
{
return ret;
}
double tmp_x = 0;
double tmp_y = 0;
for (int i = 0; i < areas.size(); i++)
{
tmp_x += points[i].x * areas[i];
tmp_y += points[i].y * areas[i];
}
ret.x = (tmp_x / totalarea);
ret.y = (tmp_y / totalarea);
return ret;
}


int main()
{
int n; //n组数据
int m; //m个点
vector<Point> Points; //存储所有的点
vector<Point> BarycentrePoints;
vector<double> Areas;
cin >> n;
while (n--)
{
cin >> m;
double tmp_x;  //用来获取每一个点的x,y坐标
double tmp_y;
double TotalArea; //获得总面积
Point tmp_Point(0, 0);
Points.clear();
Areas.clear();
BarycentrePoints.clear();
Point Zero(0, 0);
while (m--)
{
cin >> tmp_x >> tmp_y;
tmp_Point.x = tmp_x;
tmp_Point.y = tmp_y;
Points.push_back(tmp_Point);
}
//现在有了所有点的坐标,开始进行处理
//参照多边形重心算法,依次取两个点,和原点组成三角形,得到三角形面积和重心,得到若干个三角形面积重心,求加权平均数
for (int i = 0; i < Points.size(); i++)
{
Point tmp_barycentre(0, 0);
double tmp_area;
if (i != Points.size() - 1)
{
tmp_barycentre = GetTriangleBarycentre(Zero, Points[i], Points[i + 1]);
tmp_area = GetTriangleArea(Zero, Points[i], Points[i + 1]);
}
else
{
tmp_barycentre = GetTriangleBarycentre(Zero, Points[i], Points[0]);
tmp_area = GetTriangleArea(Zero, Points[i], Points[0]);
}


Areas.push_back(tmp_area);
BarycentrePoints.push_back(tmp_barycentre);
}
TotalArea = GetTotalArea(Areas);
tmp_Point = GetLastBarycentrePoint(Areas, BarycentrePoints);
cout << setprecision(3) << endl;
cout << setiosflags(ios::fixed) << fabs(TotalArea) << " " << tmp_Point.x + tmp_Point.y << endl;
}
return 0;
}




其他:

这是最开始的代码,有些待优化的地方,下午开始做的,不过本地测试通过,提交OJ Wrong answer,折腾大半天,心态爆炸了都没搞定,晚上终究放不下,和AC code对比,崩溃边缘发现通过的是double,而我用的都是float,改为double后通过,找到最开始的code,改为double后也通过,也就是说根本原因只是应该用double而不是float,深层原因以后再研究,记录下学到的一些东西。

1,float -> double

2.循环轮换,比如起那么会有判断是否到头,到头后转到第一个元素的代码,第一版用的是

if (i != Points.size() - 1)
{
tmp_barycentre = GetTriangleBarycentre(Zero, Points[i], Points[i + 1]);
tmp_area = GetTriangleArea(Zero, Points[i], Points[i + 1]);
}
else
{
tmp_barycentre = GetTriangleBarycentre(Zero, Points[i], Points[0]);
tmp_area = GetTriangleArea(Zero, Points[i], Points[0]);
}
其实改为

tmp_barycentre = GetTriangleBarycentre(Zero, Points[i], Points[(i + 1)%Points.size()]);
tmp_area = GetTriangleArea(Zero, Points[i], Points[(i + 1)%Points.size()]);

感觉更优雅,可以利用mod运算达到循环的目的。

3.在搜索相关资料时候有看到杭电的一个类似的题,感觉里面做法就比较好,那就是不需要记录所有点,只需要记录原点(最后一个点和原点组成一对)和用到的两个点即可,也有针对该方案实现

 
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>

using namespace std;

double GetArea(double x1, double y1, double x2, double y2)
{
return (x2*y1 - y2*x1) / 2.0;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int m;
cin >> m;
double x0,y0,x1, y1, x2, y2;
double area=0;
double tmp_x=0, tmp_y=0;
double tmp_area=0;
cin >> x1 >> y1;
x0 = x1;  //记录初始点
y0 = y1;
for (int i = 0; i < m; i++)
{
if (i < m - 1)
{
cin >> x2 >> y2;
}
else
{
x2 = x0;
y2 = y0;
}

tmp_area = GetArea(x1, y1, x2, y2);
area += tmp_area;
tmp_x += tmp_area*(x1 + x2) / 3.0;
tmp_y += tmp_area*(y1 + y2) / 3.0;
x1 = x2;
y1 = y2;
}
if (fabs(area) < 1e-7)
{
printf("0.000 0.000\n");
}
else
{
printf("%.3lf %.3lf\n", fabs(area), (tmp_x + tmp_y) / area);
}

}
return 0;
}
本以为会使用更少空间,结果这种方案时间/内存占用是8/312,而自己最初的方案(vector)时间/内存占用反而是4/312,不过是相对于数组方式(8/400)减少了内存使用。


4.本地用的VS2015 社区版,感觉不够严谨,或者说对用户太纵容了,有些头文件没有包含,也能正常编译运行,提交OJ就编译错误了,可惜没找大哪个能让编译要求更严谨。

原创粉丝点击