题目大意:
给你一个任意多边形的瓷砖,想用一个容器包起来。问一下会浪费多少空间。实际上就是给你任意一个多边形,求一个凸包,然后求凸包和这个任意多边形面积之差占凸包面积的比例。凸包就是外面的包装容器。所以说如果这个瓷砖本身就是一个凸多边形,那么肯定没有浪费空间,因为外面刚好用凸多边形包住。
这个题目wa了好几次,很悲剧,这个oj评判系统少打空行会wa而不是pe。题目明明说的是两个case之间打空行,没想到最后一个也要打,不符合常理啊,难道最后的0也算一个case。没必要纠结了,每一个case多打一个换行就对了。。。。。
算法设计:
这个题目主要的算法就是如何求凸包,以及如何计算多边形的面积,知道了这两个方面的知识,那么这道题目就简单了。
首先任意多边形的面积要会求: ![](http://hi.csdn.net/attachment/201109/17/0_1316245606Kcq3.gif)
具体证明这个网上一大堆,就不证明了,注意如果多边形给的边界点的顺序不一样,面积可能为负值,所以最好取一下绝对值。
凸包的算法,其实也不难,懂了以后可以作为模板使用。这里我还是简单介绍一下啊。
首先我们找到最左最下面的点,这里在代码里面就是k变量所代表的那个点。然后放到第0个点的位置。
然后求出其他点和这个左下点的连线的斜率,直接atan2()就行了。然后从第一个点开始按照斜率从小到大排序,也可以从大到小。
然后从第3个点开始,到第n-1个点,进行判断,如何判断呢?
这里要用到栈,栈里面保存的都是凸包的当前的点,如果当前这个点与栈顶的这两个点形成“凸包的形式”,那么就直接进栈,否则将栈顶的点出栈,继续判断,直到形成“凸包的形式”。
什么叫做凸包的形式?我们现在已经求出了最下最左的点,然后按照斜率从小到大排序了,所以,这个线段的走势应该是左拐才是凸包的形式,如果右拐,那么就不是。
这里是用叉积判断的。给出图片解释一下。
如A是左下点,从A到B右拐,到C是左拐,如果是左拐,那么直接进栈吧,但是如果是B的话,E就要退栈了,肯定要B。。。。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
using namespace std;
struct POINT
{
int x,y;
double angle;
}point[210],stack[210];
int n, top;
typedef struct POINT Point;
//欧氏距离
double Distance(Point p1, Point p2)
{
return sqrt((double)((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)));
}
//叉积 op_sp × op_ep
double multily(Point sp, Point ep, Point op)
{
return (double)((sp.x - op.x) * (ep.y - op.y) - (ep.x - op.x) * (sp.y - op.y));
}
//比较函数从小到大
int cmp(const void *a, const void *b)
{
Point *c = (Point *)a;
Point *d = (Point *)b;
if (c->angle > d->angle)
{
return 1;
}
else if(c->angle < d->angle)
{
return -1;
}
else
return (c->x * c->x + c->y * c->y) < (d->x * d->x + d->y * d->y) ? -1 : 1;
}
//求凸包
void Graham_scan()
{
int i;
stack[0] = point[0];
stack[1] = point[1];
stack[2] = point[2];
for (i = 3; i < n; ++ i)
{
while (multily(point[i], stack[top], stack[top - 1]) > 0)
{
top --;
}
stack[++ top] = point[i];
}
}
//求出任意多边形的面积
double getArea2()
{
double sum = 0.0;
int i;
for (i = 0; i < n; ++ i)
{
sum += (point[i].x * point[(i + 1) % n].y - point[i].y * point[(i + 1) % n].x);
}
return sum / 2.0;
}
//求出凸包的面积
double getArea1()
{
double sum = 0.0;
int i;
for (i = 0; i <= top; ++ i)
{
sum += (double)(stack[i].x * stack[(i + 1) % (top + 1)].y - stack[i].y * stack[(i + 1) % (top + 1)].x);
}
return sum / 2.0;
}
int main()
{
double area1,area2;
int cnt = 1;
int i, k;
scanf("%d", &n);
if (!n)
{
return 0;
}
while(1)
{
top = 2;
k = 0;
printf("Tile #%d\n", cnt ++);
for (i = 0; i < n; ++ i)
{
scanf("%d %d", &point[i].x, &point[i].y);
}
area2 = fabs(getArea2());
for (i = 1; i < n; ++ i)
{
if (point[i].y < point[k].y || (point[i].y == point[k].y && point[i].x < point[k].x))
{
k = i;
}
}
if (k)
{
Point tmp = point[0];
point[0] = point[k];
point[k] = tmp;
}
for (i = 1; i < n; ++ i)
{
point[i].angle = atan2((double)(point[i].y - point[0].y), (double)(point[i].x - point[0].x));
}
qsort(point + 1, n - 1, sizeof(point[0]),cmp);
Graham_scan();
area1 = fabs(getArea1());
printf("Wasted Space = %.2lf %%\n\n", (area1 - area2) * 100.0 / area1);
scanf("%d", &n);
if (n)
{
//printf("\n");
}
else
break;
}
return 0;
}