题面
给出一个n边多边形,要求构造另一个图形,使新多边形在旧多边形外部,且最近处的距离大于等于L,还要使构造的图形面积尽量小。
求新构造形状的边长
3 ≤ n ≤ 1000,1 ≤ L ≤ 1000
分析
首先要理解好题意。。。是多边形外套某图形。
很容易想到的办法是,处处与内部间隔L,这就要求了在拐角处可能会出现圆弧,经过更加细致的考虑,发现将多边形的边垂直外推L,拐角处设置圆弧即可。
但是这个问题还有两个细节:原多边形的凹凸问题,拐角处的弧长具体怎么算。
先考虑第一个问题,如果只是简单外推,发现这种外围不是最优的,红色是凹部分的外推,但是绿色才是更短的(也是最短的)。
为了避免这种凹处的外推,就消除掉原多边形的凹处,首先作原多边形的凸包,再将边进行外推。
这里的绿色,其实就是凸包外推的结果。
拐角的弧长,也就是凸包外推后连接间断点的弧,圆心是凸包的顶点
像这个,未标出的那段弧就是计入在最终长度的弧
计入长度弧长的圆心角可以发现是 180-w,每一个顶点都是这样,也就是说,最终计入的圆心角是:
n
π
−
(
n
−
2
)
π
=
2
π
n\pi-(n-2)\pi=2\pi
nπ−(n−2)π=2π,前面是因为n个顶点,后面是凸n边形的内角和
现在就发现,最终新图形的长度是由内多边形凸包的边长 + 2 π L 2\pi L 2πL构成
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<string>
#include<math.h>
#include<vector>
#include<iomanip>
using namespace std;
struct point
{
point() {}
point(double a, double b) :x(a), y(b) {}
double x, y;
int dcmp(double x)//误差取等
{
if (abs(x) < 1e-10)return 0;else return x < 0 ? -1 : 1;
}
bool operator <(const point& other)//为了sort
{
if (dcmp(x - other.x) == 0) {//x1=x2
if (y < other.y)return 1;
else return 0;
}
else
{
if (x < other.x)return 1;
else return 0;
}
}
bool operator == (const point& other)//为了unique
{
return dcmp(x - other.x) == 0 && dcmp(y - other.y) == 0;
}
};
struct Vect//向量
{
double x, y;
Vect(point& p1, point& p2) :x(p2.x - p1.x), y(p2.y - p1.y) {}//从点构造向量
double cross(Vect other)//叉积
{
return x * other.y - other.x * y;
}
};
vector<point> v;//存所有点,并进行从小到大排序
int line[1005], ptr = 0;//保存凸壳路径
void convex()//求凸壳
{
int size = v.size();
for (int i = 0; i < size; i++)//处理i
{
while (ptr > 1 && Vect(v[line[ptr - 1]], v[i]).cross(Vect(v[line[ptr - 2]], v[line[ptr - 1]])) > 0)
//求新加入的点连线 与 原本延伸方向的叉积。根据右手定则,如果是正的,则在原本延伸方向的外侧
ptr--;//后退,直到能与v[i] 形成凸壳
line[ptr++] = i;//符合,入凸包(用栈结构储存)
}
}
double dis(point& a, point& b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int main()
{
ios::sync_with_stdio(false);
int n, L;
cin >> n >> L;
double x, y;//存各个读入的点
point temp;
for (int i = 0; i < n; i++)
{
cin >> x >> y;
temp = point(x, y);
v.push_back(temp);
}
sort(v.begin(), v.end());
v.resize(unique(v.begin(), v.end()) - v.begin());//去重点
double ans = 0;//记录凸壳长度
convex();//求下凸壳
for (int i = 1; i < ptr; i++)
{
ans += dis(v[line[i - 1]], v[line[i]]);
}
ptr = 0;
reverse(v.begin(), v.end());
convex();//求上凸壳
for (int i = 1; i < ptr; i++)
{
ans += dis(v[line[i - 1]], v[line[i]]);
}
ans += 3.141592653538462643383279*2*L;
cout << fixed << setprecision(0) << ans;
return 0;
}