題面
給出一個n邊多邊形,要求構造另一個圖形,使新多邊形在舊多邊形外部,且最近處的距離大於等於L,還要使構造的圖形面積儘量小。
求新構造形狀的邊長
3 ≤ n ≤ 1000,1 ≤ L ≤ 1000
分析
首先要理解好題意。。。是多邊形外套某圖形。
很容易想到的辦法是,處處與內部間隔L,這就要求了在拐角處可能會出現圓弧,經過更加細緻的考慮,發現將多邊形的邊垂直外推L,拐角處設置圓弧即可。
但是這個問題還有兩個細節:原多邊形的凹凸問題,拐角處的弧長具體怎麼算。
先考慮第一個問題,如果只是簡單外推,發現這種外圍不是最優的,紅色是凹部分的外推,但是綠色纔是更短的(也是最短的)。
爲了避免這種凹處的外推,就消除掉原多邊形的凹處,首先作原多邊形的凸包,再將邊進行外推。
這裏的綠色,其實就是凸包外推的結果。
拐角的弧長,也就是凸包外推後連接間斷點的弧,圓心是凸包的頂點
像這個,未標出的那段弧就是計入在最終長度的弧
計入長度弧長的圓心角可以發現是 180-w,每一個頂點都是這樣,也就是說,最終計入的圓心角是:
,前面是因爲n個頂點,後面是凸n邊形的內角和
現在就發現,最終新圖形的長度是由內多邊形凸包的邊長 + 構成
代碼
#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;
}