Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 31844 | Accepted: 10756 |
Description

Your task is to help poor Architect to save his head, by writing a program that will find the minimum possible length of the wall that he could build around the castle to satisfy King's requirements.
The task is somewhat simplified by the fact, that the King's castle has a polygonal shape and is situated on a flat ground. The Architect has already established a Cartesian coordinate system and has precisely measured the coordinates of all castle's vertices in feet.
Input
Next N lines describe coordinates of castle's vertices in a clockwise order. Each line contains two integer numbers Xi and Yi separated by a space (-10000 <= Xi, Yi <= 10000) that represent the coordinates of ith vertex. All vertices are different and the sides of the castle do not intersect anywhere except for vertices.
Output
Sample Input
9 100 200 400 300 400 300 300 400 300 400 400 500 400 500 200 350 200 200 200
Sample Output
1628
凸包:
(1)问题:
给定二维平面点集,求最小的包含所有点的凸多边形。
(2)Gramham-Scan算法:
Gramham-Scan是一种灵活的凸包算法,其总的时间复杂度仅为O(n*log(n))。
步骤:
Step1: 选定x坐标最小(相同情况y最小)的点作为极点,这个点必在凸包上;
Step2: 将其余点按极角排序,在极角相同的情况下比较与极点的距离,离极点比较近的优先;
Step3: 用一个栈S存储凸包上的点,先将按极角和极点排序最小的两个点入栈;
Step4: 按序扫描每个点,检查栈顶的两个元素与这个点构成的折线段是否“拐”向右侧(叉积小于等于零);
Step5: 如果满足,则弹出栈顶元素,并返回Step4再次检查,直至不满足。将该点入栈,并对其余点不断执行此操作;
Step6: 最终栈中元素为凸包的顶点序列。
解题思路:
这道题的答案是凸包周长加上一个圆周长,即包围凸包的一个圆角多边形,但是没弄明白那些圆角加起来为什么恰好是一个圆。每个圆角是以凸包对应的顶点为圆心,给定的L为半径,与相邻两条边的切点之间的一段圆弧。每个圆弧的两条半径夹角与对应的凸包的内角互补。假设凸包有n条边,则所有圆弧角之和为180°*n-180°*(n-2)=360°。故,围墙周长为=n条平行于凸包的线段+n条圆弧的长度=凸包周长+围墙离城堡距离L为半径的圆周长.
模版:(来自kuangbin大神)
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=1000;
const double PI=acos(-1.0);
struct point
{
int x,y;
};
point list[MAXN];
int stack[MAXN],top;
int cross(point p0,point p1,point p2) //计算叉积p0p1×p0p2
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2) //计算p1p2的距离
{
return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数,角度相同则距离小的在前面
{
int tmp=cross(list[0],p1,p2);
if(tmp>0) return true;
else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
else return false;
}
void init(int n) //输入,并把最左下方的点放在list[0],并且进行极角排序
{
int i,k;
point p0;
scanf("%d%d",&list[0].x,&list[0].y);
p0.x=list[0].x;
p0.y=list[0].y;
k=0;
for(i=1;i<n;i++)
{
scanf("%d%d",&list[i].x,&list[i].y);
if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)))
{
p0.x=list[i].x;
p0.y=list[i].y;
k=i;
}
}
list[k]=list[0]; //选取最左下角的点为极点(起始点),交换位置
list[0]=p0;
sort(list+1,list+n,cmp);
}
void graham(int n)
{
int i;
if(n==1) {top=0;stack[0]=0;}
if(n==2)
{
top=1;
stack[0]=0;
stack[1]=1;
}
if(n>2)
{
for(i=0;i<=1;i++) stack[i]=i;
top=1;
for(i=2;i<n;i++)
{
//栈顶和栈顶下面那个元素和当前遍历的元素的叉积,栈顶下面那个元素的下标是最小的,假设p1p2,p1p3,如果p1p2在p1p3的逆时针那么就不满足条件
while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0) top--;
top++;
stack[top]=i; //否则插入当前栈顶
}
}
}
int main()
{
int N,L;
//freopen("111","r",stdin);
while(scanf("%d%d",&N,&L)!=EOF)
{
init(N);
graham(N);
double res=0;
for(int i=0;i<top;i++)
res+=dis(list[stack[i]],list[stack[i+1]]); //加入栈的元素就是凸包上的顶点.
res+=dis(list[stack[0]],list[stack[top]]);
res+=2*PI*L;
printf("%d\n",(int)(res+0.5));
}
return 0;
}
Surround the Trees
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 8636 Accepted Submission(s): 3326
The diameter and length of the trees are omitted, which means a tree can be seen as a point. The thickness of the rope is also omitted which means a rope can be seen as a line.

There are no more than 100 trees.
Zero at line for number of trees terminates the input for your program.
9 12 7 24 9 30 5 41 9 80 7 50 87 22 9 45 1 50 7 0
243.06
本题注意:
n = 1和n = 2的时候要特判不能套凸包模版,还有注意精度问题.
Hdu1392 AC代码:
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=1000;
const double PI=acos(-1.0);
struct point
{
double x,y;
};
point list[MAXN];
int stack[MAXN],top;
double cross(point p0,point p1,point p2) //计算叉积p0p1×p0p2
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2) //计算p1p2的距离
{
return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数,角度相同则距离小的在前面
{
double tmp=cross(list[0],p1,p2);
if(tmp>0.0) return true;
else if(tmp==0.0&&dis(list[0],p1)<dis(list[0],p2)) return true;
else return false;
}
void init(int n) //输入,并把最左下方的点放在list[0],并且进行极角排序
{
int i,k;
point p0;
scanf("%lf%lf",&list[0].x,&list[0].y);
p0.x=list[0].x;
p0.y=list[0].y;
k=0;
for(i=1;i<n;i++)
{
scanf("%lf%lf",&list[i].x,&list[i].y);
if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)))
{
p0.x=list[i].x;
p0.y=list[i].y;
k=i;
}
}
list[k]=list[0]; //选取最左下角的点为极点(起始点),交换位置
list[0]=p0;
sort(list+1,list+n,cmp);
}
void graham(int n)
{
int i;
if(n==1) {top=0;stack[0]=0;}
if(n==2)
{
top=1;
stack[0]=0;
stack[1]=1;
}
if(n>2)
{
for(i=0;i<=1;i++) stack[i]=i;
top=1;
for(i=2;i<n;i++)
{
//栈顶和栈顶下面那个元素和当前遍历的元素的叉积,栈顶下面那个元素的下标是最小的,假设p1p2,p1p3,如果p1p2在p1p3的逆时针那么就不满足条件
while(top>0 && cross(list[stack[top-1]],list[stack[top]],list[i])<=0) top--;
top++;
stack[top]=i; //否则插入当前栈顶
}
}
}
int main()
{
int m;
//freopen("1.txt","r",stdin);
while(cin>>m && m)
{
init(m);
graham(m);
double res=0;
if(m == 1)
{
printf("0.00\n");
}
else if(m == 2)
{
printf("%.2lf\n",dis(list[0],list[1]));
}
else
{
for(int i=0;i<top;i++)
res+=dis(list[stack[i]],list[stack[i+1]]); //加入栈的元素就是凸包上的顶点.
res+=dis(list[stack[0]],list[stack[top]]);
printf("%.2lf\n",res);
}
}
return 0;
}