洛谷 P2116 城墙

题面

给出一个n边多边形,要求构造另一个图形,使新多边形在旧多边形外部,且最近处的距离大于等于L,还要使构造的图形面积尽量小。
求新构造形状的边长
3 ≤ n ≤ 1000,1 ≤ L ≤ 1000

分析

首先要理解好题意。。。是多边形外套某图形。

很容易想到的办法是,处处与内部间隔L,这就要求了在拐角处可能会出现圆弧,经过更加细致的考虑,发现将多边形的边垂直外推L,拐角处设置圆弧即可。

但是这个问题还有两个细节:原多边形的凹凸问题,拐角处的弧长具体怎么算。

先考虑第一个问题,如果只是简单外推,发现这种外围不是最优的,红色是凹部分的外推,但是绿色才是更短的(也是最短的)。
为了避免这种凹处的外推,就消除掉原多边形的凹处,首先作原多边形的凸包,再将边进行外推。
这里的绿色,其实就是凸包外推的结果。

拐角的弧长,也就是凸包外推后连接间断点的弧,圆心是凸包的顶点

像这个,未标出的那段弧就是计入在最终长度的弧
计入长度弧长的圆心角可以发现是 180-w,每一个顶点都是这样,也就是说,最终计入的圆心角是:
n π − ( n − 2 ) π = 2 π n\pi-(n-2)\pi=2\pi nπ(n2)π=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值