Wall
http://acm.hdu.edu.cn/showproblem.php?pid=1348
题目描述:有个国王想在他的城堡外面修围墙,围墙与城堡的最小距离为L,要求围墙长度最短。求围墙的长度。(哎~~,每个人心里都有一座墙,King可不例外。)
算法:围墙可看作城堡(输入的顶点)构成的凸包,各边长度不变,向外平移L,各个角的围墙拼起来就是一个半径为L的完整的圆。
就那题目给的图来说吧
不得不说本人学的PS还是很有用的。。。
先贴这题的源代码
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const double eps=1e-;
struct Node
{
int x,y;
Node operator-(Node &node) //运算符重载,需要用C++提交
{
Node new_node;
new_node.x=x-node.x;
new_node.y=y-node.y;
return new_node;
}
Node operator+(Node &node)
{
Node new_node;
new_node.x=x+node.x;
new_node.y=y+node.y;
return new_node;
}
};
vector<Node> s; //这里用vector来模拟栈,相比用stack,vector可以访问非栈顶元素,比较方便
Node *p; void swap(Node &a,Node &b)
{
Node temp;
temp=a;
a=b;
b=temp;
}
double cross(Node vec1,Node vec2)
{
return (double)vec1.x*vec2.y-(double)vec1.y*vec2.x;
}
double Distance(Node a,Node b)
{
return sqrt(((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y))*1.0);
}
bool cmp(Node &p1,Node &p2)
{
double temp=cross(p1-p[],p2-p[]);
if(temp>eps)
return true;
else if(temp<-eps)
return false;
else
{
if(Distance(p1,p[])<Distance(p2,p[]))
return true;
else
return false;
}//当2个向量共线时temp<=eps,与p[0]距离大的点排在后面
}
int lowleft(Node p[],int n)
{
int px=p[].x;
int py=p[].y;
int k=;
for(int i=;i<n;i++) //先最下再最左
{
if(p[i].y<py)
{
px=p[i].x;
py=p[i].y;
k=i;
}
else if(p[i].y==py&&p[i].x<px)
{
px=p[i].x;
k=i;
}
}
return k;
}
void graham(Node p[],int n)
{
int k=lowleft(p,n);
swap(p[],p[k]);
sort(p+,p+n,cmp); //给p[1]~p[n]按相对p[0]的极角排序(逆时针)
p[n]=p[];
s.push_back(p[]);
s.push_back(p[]); int top=;
for(int i=;i<=n;i++)
{
//检查不向左转的情况,而不是对向右转进行检查,
//这样的测试排除在所形成的凸包的某个顶点处于平角的可能性
while(top>=&&cross(s[top]-s[top-],p[i]-s[top])<=) //先判断top>=1,防止出现s[-1];
{
s.pop_back();
top--;
}
s.push_back(p[i]);
top++;
}
}
int main()
{
int t,n,L;
cin>>t;
while(t--)
{
s.clear();
double sum=0.0;
cin>>n>>L;
p=new Node[n+]; //最后一个元素存p[0]
for(int i=;i<n;i++)
cin>>p[i].x>>p[i].y;
graham(p,n);
int s_len=s.size();
for(int i=;i<s_len;i++)
{
sum+=Distance(s[i-],s[i]);
}
sum+=*acos(-1.0)*L;
sum=(int)(sum+0.5);
cout<<sum<<endl;
if(t)
cout<<endl;
}
return ;
}
凸包:形象一点说,把点集Q中的每个点都想象成是露在一块板外的铁钉,那么凸包就是包围了所有这些铁钉的一条拉紧了的橡皮绳所构成的形状。(来自算法导论)
传说寻找凸包有很多方法,我才刚学了一种,Graham扫描法。我很笨拙,花了一天时间。后期学了其他方法,会有一些更新。
Graham扫描法的主要步骤:
1.先找到最下面然后最靠左边的点。
2.以它为原点,对其他点进行极角排序(这里是逆时针)。
3.将p[0],p[1]入栈,p[0]必为凸多边形的顶点,而p[1]可能处为平角。
4.按p[0]~p[n]依次去走每个点,如果在某个点准备向右转或者直走,就将这个点出栈
下面贴出Graham算法的模板:
void graham(Node p[],int n)
{
int k=lowleft(p,n);
swap(p[],p[k]);
sort(p+,p+n,cmp); //给p[1]~p[n]按相对p[0]的极角排序(逆时针)
p[n]=p[];
s.push_back(p[]);
s.push_back(p[]); int top=;
for(int i=;i<=n;i++)
{
//检查不向左转的情况,而不是对向右转进行检查,
//这样的测试排除在所形成的凸包的某个顶点处于平角的可能性
while(top>=&&cross(s[top]-s[top-],p[i]-s[top])<=) //先判断top>=1,防止出现s[-1];
{
s.pop_back();
top--;
}
s.push_back(p[i]);
top++;
}
}