1670: [Usaco2006 Oct]Building the Moat护城河的挖掘
Time Limit: 3 Sec
Memory Limit: 64 MB
Submit: 304
Solved: 221
[ Submit][ Status][ Discuss]
Description
为了防止口渴的食蚁兽进入他的农场,Farmer John决定在他的农场周围挖一条护城河。农场里一共有N(8<=N<=5,000)股泉水,并且,护城河总是笔直地连接在河道上的相邻的两股泉水。护城河必须能保护所有的泉水,也就是说,能包围所有的泉水。泉水一定在护城河的内部,或者恰好在河道上。当然,护城河构成一个封闭的环。 挖护城河是一项昂贵的工程,于是,节约的FJ希望护城河的总长度尽量小。请你写个程序计算一下,在满足需求的条件下,护城河的总长最小是多少。 所有泉水的坐标都在范围为(1..10,000,000,1..10,000,000)的整点上,一股泉水对应着一个唯一确定的坐标。并且,任意三股泉水都不在一条直线上。 以下是一幅包含20股泉水的地图,泉水用"*"表示
图中的直线,为护城河的最优挖掘方案,即能围住所有泉水的最短路线。 路线从左上角起,经过泉水的坐标依次是:(18,0),(6,-6),(0,-5),(-3,-3),(-17,0),(-7,7),(0,4),(3,3)。绕行一周的路径总长为70.8700576850888(...)。答案只需要保留两位小数,于是输出是70.87。
Input
* 第1行: 一个整数,N * 第2..N+1行: 每行包含2个用空格隔开的整数,x[i]和y[i],即第i股泉水的位 置坐标
Output
* 第1行: 输出一个数字,表示满足条件的护城河的最短长度。保留两位小数
Sample Input
20
2 10
3 7
22 15
12 11
20 3
28 9
1 12
9 3
14 14
25 6
8 1
25 1
28 4
24 12
4 15
13 5
26 5
21 11
24 4
1 8
Sample Output
70.87
HINT
Source
凸包 卡壳
果然刚上高一的我就是弱呀。。。凸包学了1h+(主要是理解向量计算。。。。)
以左下角为(0,0),每个点都以(x,y)的方法表示,那么每个单位就可以表示那个点或者是起点到它的向量。。
向量是有运算法则的。。。。。。。。。。。。。。。。。。
本题使用Andrew算法,先将所有点按照x从小到大排序,对于x相同的再按y从小到大排序
先将一开始的两个点扔进凸包,然后一个个点去找,如果当前点与上个点的向量在当前路径右侧,
则删除路径上的点直到当前点在当前路径的左侧,反着再来一遍
复杂度O(nlogn)
用叉积来判断两个向量的位置关系(xA*yB-xB*yA)。。。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 5e3 + 50;
typedef double DB;
struct P{
int x,y;
P operator - (const P &b)
{
return (P) {x - b.x,y - b.y};
}
bool operator < (const P &b) const{
if (x < b.x) return 1;
if (x > b.x) return 0;
return y < b.y;
}
bool operator != (const P &b) const{
return x != b.x || y != b.y;
}
}poi[maxn],edgs[4*maxn];
int Cross (P A,P B)
{
return 1LL*A.x * B.y - 1LL*A.y * B.x;
}
DB det (P x,P y)
{
long long A = x.x - y.x;
long long B = x.y - y.y;
return sqrt ((A*A) + (B*B));
}
int n,i,j,cur = 1;
int main()
{
#ifdef YZY
freopen("yzy.txt","r",stdin);
#endif
cin >> n;
for (i = 1; i <= n; i++) scanf("%d%d",&poi[i].x,&poi[i].y);
sort (poi + 1,poi + n + 1);
for (i = 2; i <= n; i++)
if (poi[i] != poi[i-1])
poi[++cur] = poi[i];
int Siz = 0;
for (i = 1; i <= cur; i++)
{
while (Siz > 1 && Cross(edgs[Siz] - edgs[Siz-1],poi[i] - edgs[Siz-1]) <= 0) Siz--;
edgs[++Siz] = poi[i];
}
int down = Siz;
for (i = cur - 1; i > 0; i--)
{
while (Siz > down && Cross(edgs[Siz] - edgs[Siz-1],poi[i] - edgs[Siz-1]) <= 0) Siz--;
edgs[++Siz] = poi[i];
}
DB ans = 0;
for (i = 1; i < Siz; i++) ans += det (edgs[i],edgs[i + 1]);
printf("%.2lf",ans);
return 0;
}