poj 1113

题目概述:

有N个点,一个距离参数L

每个点坐标x,y

由这些点构造一个周长最短的图形使得所有点不在该图形外部,再构造另一个周长最短图形,使得旧图形在新图形内部且新图形上任何点到旧图形的距离等于L

输入:

第一行N,L,其后N行,每行x,y

输入只有一组

限制:

3<=N<=1000;1<=L<=1000;-10000<=x,y<=10000;

输出:

一个整数,新图形的周长,小数部分四舍五入到整数

输出只有一组

样例输入:

9 100
200 400
300 400
300 300
400 300
400 400
500 400
500 200
350 200
200 200

样例输出:

1628

讨论:

经典的求凸包老题,利用melkman算法,有些多余,但该算法是在线算法,任何时刻的deque数组的bot+1到top-1下标的点都构成凸包

题解状态:

164K,16MS,C++,1448B

#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;
#define INF 0x3f3f3f3f
#define maxx(a,b) ((a)>(b)?(a):(b))
#define minn(a,b) ((a)<(b)?(a):(b))
#define MAXN 1005

const double pi = atan(1.0) * 4;//比起任何常数,还是这个比较靠谱
struct point//点(或向量)的结构体,表示其横纵坐标
{
	int x;
	int y;
};
point points[MAXN];//原始数据存放于此
int deque[MAXN * 2];//处理算法的数组,类似于STL的双头队列(然而实质是循环队列)而借其名,然而STL的只能读取两头的元素,不能满足该题需要,*2则是扩张的需要,因为是从n处开始,因此两侧都可能用到,另外注意这里存的是对应points数组的下标,起到一个指针的作用
int top, bot;//top,bottom,代表队头和队尾,最后也作为算法结果的一部分
int N, L;//题目原生变量,N个点,距离L
inline double dis(point &a, point &b)//distance,两点间距离
{
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx*dx + dy*dy);
}
inline int xproduct(point &a, point &b)//cross product,向量积的z坐标,但由于只有平面坐标系,可仅考虑其正负的意义
{
	return a.x*b.y - a.y*b.x;
}
inline int isleft(point &a, point &b, point &c)//判断点c是否在向量ab的逆时针方向,若在则返回一正数,若在顺时针方向则返回一负数,若c在直线ab上,返回0
{
	point p, o;
	p.x = b.x - a.x;
	p.y = b.y - a.y;
	o.x = c.x - b.x;
	o.y = c.y - b.y;
	return xproduct(p, o);//其实返回的是向量ab和向量bc的叉乘
}
inline void melkman(void)
{
	bot = N - 1;
	top = N;//首先定义队尾和队头
	deque[top++] = 0;//假设数据合法,至少有3个点,先将前两个点入队
	deque[top++] = 1;<span id="transmark"></span>
	int p;//这个临时变量需要一直使用,因此没有在for内声明
	for (p = 2; !isleft(points[deque[top - 2]], points[deque[top - 1]], points[p]); p++)//找一个点使得最初这三点不共线
		deque[top - 1] = p;//注意,如果共线,会用找的点取代原来作为第二个参数的点,经过有序化的点进行这一操作是有意义的
	deque[bot--] = p;
	deque[top++] = p;//压入第三个节点,并作为起点/终点
	if (isleft(points[deque[N]], points[deque[N + 1]], points[deque[N + 2]]) < 0) {//确保起始的三个点是逆时针顺序的,因为算法的结果就是逆时针顺序,而前三个点无法用算法主体判断
		deque[N] ^= deque[N + 1];
		deque[N + 1] ^= deque[N];
		deque[N] ^= deque[N + 1];//这里用异或运算只是提醒一下存在这种方法交换数值变量
	}
	for (p++; p < N; p++) {//遍历剩余所有点,构造凸包
		if (isleft(points[deque[top - 2]], points[deque[top - 1]], points[p]) <= 0 || isleft(points[deque[bot + 1]], points[deque[bot + 2]], points[p]) <= 0) {//如果新点在开头/末尾两个点非顺时针方向
			while (isleft(points[deque[top - 2]], points[deque[top - 1]], points[p]) <= 0)
				top--;//如果新点在旧凸包最后两个点所成向量的顺时针方向或共线,则原来最后一个点会在新凸包内部或边上,退掉这个点
			deque[top++] = p;//新点在逆时针方向,确实可构成凸包
			while (isleft(points[deque[bot + 1]], points[deque[bot + 2]], points[p]) <= 0)
				bot++;//对于最初两个点同理
			deque[bot--] = p;//同理
		}
	}
}
double fun()
{
	for (int p = 0; p < N; p++) {
		scanf("%d%d", &points[p].x, &points[p].y);//input
	}//input ends here
	melkman();
	double sum = 0.0;
	for (int p = bot + 1; p < top - 1; p++)
		sum += dis(points[deque[p]], points[deque[p + 1]]);
	sum += 2 * pi*L;
	return sum;
}
int main(void)
{
	//freopen("vs_cin.txt", "r", stdin);
	//freopen("vs_cout.txt", "w", stdout);

	while (~scanf("%d%d", &N, &L)) {//input
		printf("%.0f\n", fun());//output
	}
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值