1. 程式人生 > 實用技巧 >P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二維凸包

P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二維凸包

題目描述

農夫約翰想要建造一個圍欄用來圍住他的奶牛,可是他資金匱乏。他建造的圍欄必須包括他的奶牛喜歡吃草的所有地點。對於給出的這些地點的座標,計算最短的能夠圍住這些點的圍欄的長度。

輸入格式

輸入資料的第一行是一個整數。表示農夫約翰想要圍住的放牧點的數目nn。

22到第(n + 1)(n+1)行,每行兩個實數,第(i + 1)(i+1)行的實數x_i, y_ixi,yi分別代表第ii個放牧點的橫縱座標。

輸出格式

輸出輸出一行一個四捨五入保留兩位小數的實數,代表圍欄的長度。

輸入輸出樣例

輸入 #1
4
4 8
4 12
5 9.3
7 8
輸出 #1
12.00

說明/提示

資料規模與約定

對於100\%100%的資料,保證1 \leq n \leq 10^51n105,-10^6 \leq x_i, y_i \leq 10^6106xi,yi106。小數點後最多有22位數字。

程式碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+7;
struct node
{
    double x,y;
}p[maxn],s[maxn];//p用於存原始點,s用於可能存在凸殼上的點並用來模擬棧

double d(node a,node b)//距離
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double check(node a1,node a2,node b1,node b2)//叉積(大於零時為逆時針,等於零平行,小於零順時針) { return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y); } int cmp(node a,node b)//按照極角的大小逆時針排序(與x軸正方向) { if(check(p[1],a,p[1],b)>0)return 1; if(check(p[1],a,p[1],b)==0&&d(p[0],a)<d(p[0],b))return 1; return
0; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); if(i!=1&&p[i].y<p[1].y)swap(p[i],p[1]);//取y最小的點作為凸包的第一個點 } sort(p+2,p+n+1,cmp); s[1]=p[1];//y最小的點一定在凸殼上 int cnt=1; for(int i=2;i<=n;i++) { while(cnt>1&&check(s[cnt-1],s[cnt],s[cnt],p[i])<=0)cnt--;//當前點如果與在它之前加入凸殼的前兩個點形成了一個朝左的凹殼,則說明中間的那個點可以不要 cnt++; s[cnt]=p[i];//入棧 } s[cnt+1]=p[1];//回到最初點 double ans=0; for(int i=1;i<=cnt;i++)ans+=d(s[i],s[i+1]); printf("%.2f",ans); return 0; }