P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows
题目描述
农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏。他建造的围栏必须包括他的奶牛喜欢吃草的所有地点。对于给出的这些地点的坐标,计算最短的能够围住这些点的围栏的长度。
输入输出格式
输入格式:
输入数据的第一行包括一个整数 N。N(0 <= N <= 10,000)表示农夫约翰想要围住的放牧点的数目。接下来 N 行,每行由两个实数组成,Xi 和 Yi,对应平面上的放牧点坐标(-1,000,000 <= Xi,Yi <= 1,000,000)。数字用小数表示。
输出格式:
输出必须包括一个实数,表示必须的围栏的长度。答案保留两位小数。
输入输出样例
输入样例#1: 复制
4 4 8 4 12 5 9.3 7 8
输出样例#1: 复制
12.00
说明
题目翻译来自NOCOW。
分析:凸包+求相邻线段长度(主要是凸包的求法)
代码:
/**
*
* 凸包:求周长
* 奶牛栅栏多长
* AC通过
*
*/
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static double x[];
static double y[];
static int n;
//这是存凸包点的一种方式
static int count;//表示凸包数组中有count个元素
static int ch[]=new int [n];
//这是存凸包点的第二种方式
static ArrayList<Integer> a=new ArrayList();
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
x=new double[n];
y=new double[n];
for(int i=0;i<n;i++)
{
x[i]=sc.nextDouble();
y[i]=sc.nextDouble();
}
baoguo();
double s=0;
for(int i=1;i<a.size();i++)
{
s+=Distance(a.get(i)-1,a.get(i));
}
s+=Distance(a.get(0),a.get(a.size()-1));
System.out.printf("%.2f",s);
}
//给我Point点的位置即可
static int Direction(int a,int b,int c)
{
double d=(x[b]-x[a])*(y[c]-y[a])-(x[c]-x[a])*(y[b]-y[a]);
if(d==0) return 0;
else if(d>0) return 1;
else return -1;
}
static double Distance(int a,int b)
{
return Math.sqrt((x[b]-x[a])*(x[b]-x[a])+(y[b]-y[a])*(y[b]-y[a]));
}
static void baoguo()
{
//选最左最下的点
int index=0;//标记最左最小下标
double min=x[0];
int tmp;
int k;
for(int i=0;i<n;i++)
{
if(min>x[i]||min==x[i]&&y[index]>y[i])
{
min=x[i];
index=i;
}
}
tmp=index;
while(true)
{
k=-1;
a.add(index);
for(int i=0;i<n;i++)
{
if(i!=index&&(k==-1||(Direction(index,i,k)>0||Direction(index,i,k)==0&&(Distance(index,k)<Distance(index,i)))))
{
k=i;
}
}
if(k==tmp)
break;
index=k;
}
}
}