洛谷 P2116 城牆

題面

給出一個n邊多邊形,要求構造另一個圖形,使新多邊形在舊多邊形外部,且最近處的距離大於等於L,還要使構造的圖形面積儘量小。
求新構造形狀的邊長
3 ≤ n ≤ 1000,1 ≤ L ≤ 1000

分析

首先要理解好題意。。。是多邊形外套某圖形。

很容易想到的辦法是,處處與內部間隔L,這就要求了在拐角處可能會出現圓弧,經過更加細緻的考慮,發現將多邊形的邊垂直外推L,拐角處設置圓弧即可。

但是這個問題還有兩個細節:原多邊形的凹凸問題,拐角處的弧長具體怎麼算。

先考慮第一個問題,如果只是簡單外推,發現這種外圍不是最優的,紅色是凹部分的外推,但是綠色纔是更短的(也是最短的)。
爲了避免這種凹處的外推,就消除掉原多邊形的凹處,首先作原多邊形的凸包,再將邊進行外推。
這裏的綠色,其實就是凸包外推的結果。

拐角的弧長,也就是凸包外推後連接間斷點的弧,圓心是凸包的頂點

像這個,未標出的那段弧就是計入在最終長度的弧
計入長度弧長的圓心角可以發現是 180-w,每一個頂點都是這樣,也就是說,最終計入的圓心角是:
nπ(n2)π=2πn\pi-(n-2)\pi=2\pi,前面是因爲n個頂點,後面是凸n邊形的內角和

現在就發現,最終新圖形的長度是由內多邊形凸包的邊長 + 2πL2\pi 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章