《算法竞赛·快冲300题》每日一题:“围墙”

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。

文章目录

  • 题目描述
  • 题解
  • C++代码
  • Java代码
  • Python代码

围墙” ,链接: http://oj.ecustacm.cn/problem.php?id=1138

题目描述

【题目描述】 M国有n个城镇,为了保护城镇的村民,国王想建造一堵围墙。
这堵围墙要将所有城镇围住,并且离最近的城镇的距离至少为L,因为村民希望有一定的活动范围。
国王想知道这堵围墙的长度最少是多少,由于国王日理万机,他把这个问题交给你来解决。
【输入格式】 输入第一行有两个整数n和L(3≤n≤1000,1≤L≤1000)
接下来n行,每行两个整数Xi ,Yi,表示第i个城镇的坐标。(−10000≤x,y≤10000)
【输出格式】 输出一行表示围墙的长度。(答案精确到整数)
【输入样例】

9 100
200 400
300 400
300 300
400 300
400 400
500 400
500 200
350 200
200 200

【输出样例】

1628

题解

   把每个城镇看成平面图上的点,那么围墙和凸包有关。
   首先求出凸包,例如下图凸包上有4个点。为了求围墙,以4个点为圆心,以L为半径画出4个圆,用切线连接4个圆,最外围的线条就是最短的围墙。
《算法竞赛·快冲300题》每日一题:“围墙”_第1张图片
   围墙的长度等于4根切线的长度,加上4个圆上的圆弧的长度。
   容易观察得到,这4段圆弧合起来是一个完整的圆。所以,最小围墙的长度 = 凸包长度 + 一个圆周长度。
   本题是凸包的裸题,直接套凸包的模板(模板代码见《算法竞赛》清华大学出版社,罗勇军,郭卫斌著,523页,“8.1.5 凸包”)。
【重点】 凸包。

C++代码

#include 
using namespace std;
const int N = 1e5+1;
const double pi = acos(-1.0);
const double eps = 1e-6;
int sgn(double x){        //判断x是否等于0
    if(fabs(x) < eps)  return 0;
    else return x<0?-1:1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double x, double y):x(x),y(y){}
    Point operator + (Point B){return Point(x+B.x,y+B.y);}
    Point operator - (Point B){return Point(x-B.x,y-B.y);}
    bool operator == (Point B){return sgn(x-B.x) == 0 && sgn(y-B.y) == 0;}
    bool operator < (Point B){                  //用于sort()排序,先按x排序,再按y排序
        return sgn(x-B.x)<0 || (sgn(x-B.x)==0 && sgn(y-B.y)<0);}
};
typedef Point Vector;
double Cross(Vector A,Vector B){return A.x*B.y - A.y*B.x;}      //叉积
double Distance(Point A,Point B){return hypot(A.x-B.x,A.y-B.y);}
//Convex_hull()求凸包。凸包顶点放在ch中,返回值是凸包的顶点数
int Convex_hull(Point *p,int n,Point *ch){
    sort(p,p+n);            //对点排序:按x从小到大排序,如果x相同,按y排序
    int v=0;
	//求下凸包。如果p[i]是右拐弯的,这个点不在凸包上,往回退
    for(int i=0;i<n;i++){
        while(v>1 && sgn(Cross(ch[v-1]-ch[v-2],p[i]-ch[v-1]))<=0)  v--;
        ch[v++]=p[i];
    }
    int j=v;
	//求上凸包
    for(int i=n-2;i>=0;i--){
        while(v>j && sgn(Cross(ch[v-1]-ch[v-2],p[i]-ch[v-1]))<=0)  v--;
        ch[v++]=p[i];
    }
    if(n>1) v--;
    return v;                      //返回值v是凸包的顶点数
}
Point p[N],ch[N];                  //输入点是p[],计算得到的凸包顶点放在ch[]中
int main(){
    int n,r;   cin >> n>>r;
    for(int i=0;i<n;i++)  scanf("%lf%lf",&p[i].x,&p[i].y);
    int v = Convex_hull(p,n,ch);    //返回凸包的顶点数v
    double ans=0;
    for(int i=0;i<v;i++)   ans += Distance(ch[i],ch[(i+1)%v]);  //计算凸包周长
    ans += 2.0 * pi * r;                     //加上一个圆周长
    printf("%d\n", (int)round(ans));         //四舍五入取整
    return 0;
}

Java代码

import java.util.*;
 
class Main {
    static final double eps = 1e-6;
 
    static int sgn(double x) {      
        if(Math.abs(x) < eps)        return 0;
        else           return x < 0 ? -1 : 1;
    }
 
    static class Point {
        double x, y; 
        public Point() {} 
        public Point(double x, double y) { this.x = x;  this.y = y;  } 
        public Point add(Point B) { return new Point(x + B.x, y + B.y);    } 
        public Point subtract(Point B) {return new Point(x - B.x, y - B.y); } 
        public boolean equals(Point B) {return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; } 
        public boolean lessThan(Point B) {return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0); }
    }
 
    static double cross(Point A, Point B) {return A.x * B.y - A.y * B.x;  }
 
    static double distance(Point A, Point B) {return Math.hypot(A.x - B.x, A.y - B.y);}
 
    static int convexHull(Point[] p, int n, Point[] ch) {
        Arrays.sort(p, 0, n, new Comparator<Point>() {
            public int compare(Point a, Point b) {
                if (Math.abs(a.x - b.x) > eps)    return Double.compare(a.x, b.x);
                return Double.compare(a.y, b.y);
            }
        });
 
        int v = 0;
        for (int i = 0; i < n; i++) {
            while (v > 1 && sgn(cross(ch[v - 1].subtract(ch[v - 2]), p[i].subtract(ch[v - 1]))) <= 0)
                v--;
            ch[v++] = p[i];
        }
        int j = v;
        for (int i = n - 2; i >= 0; i--) {
            while (v > j && sgn(cross(ch[v - 1].subtract(ch[v - 2]), p[i].subtract(ch[v - 1]))) <= 0)
                v--;
            ch[v++] = p[i];
        }
        if (n > 1)  v--;
        return v;
    } 
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int r = scanner.nextInt(); 
        Point[] p = new Point[n + 10];
        for (int i = 0; i < n; i++) 
            p[i] = new Point(scanner.nextDouble(), scanner.nextDouble()); 
        Point[] ch = new Point[n + 10];
        int v = convexHull(p, n, ch); 
        double ans = 0;
        for (int i = 0; i < v; i++)  ans += distance(ch[i], ch[(i + 1) % v]); 
        ans += 2.0 * Math.PI * r;
        System.out.println((int) Math.round(ans));
    }
}

Python代码

import math
N = 100001
eps = 1e-6
def sgn(x):
    if abs(x) < eps:   return 0
    else:        return -1 if x < 0 else 1
class Point:
    def __init__(self, x=0, y=0):  self.x = x;    self.y = y
    def __add__(self, other):        return Point(self.x + other.x, self.y + other.y)
    def __sub__(self, other):        return Point(self.x - other.x, self.y - other.y)
    def __eq__(self, other):        return sgn(self.x - other.x) == 0 and sgn(self.y - other.y) == 0
    def __lt__(self, other):        return sgn(self.x - other.x) < 0 or (sgn(self.x - other.x) == 0 and sgn(self.y - other.y) < 0)
def Cross(A, B):   return A.x * B.y - A.y * B.x
def Distance(A, B):   return math.hypot(A.x - B.x, A.y - B.y)
def Convex_hull(p, n, ch):  # Convex_hull()求凸包。凸包顶点放在ch中,返回值是凸包的顶点数
    p = sorted(p[:n])
    v = 0
    # 求下凸包。如果p[i]是右拐弯的,这个点不在凸包上,往回退
    for i in range(n):
        while v > 1 and sgn(Cross(ch[v-1]-ch[v-2], p[i]-ch[v-1])) <= 0:  v -= 1
        ch[v] = p[i]
        v += 1
    j = v
    # 求上凸包
    for i in range(n-2, -1, -1):
        while v > j and sgn(Cross(ch[v-1]-ch[v-2], p[i]-ch[v-1])) <= 0:  v -= 1
        ch[v] = p[i]
        v += 1
    if n > 1:     v -= 1
    return v

p = [Point() for _ in range(N)]
ch = [Point() for _ in range(N)]
n, r = map(int, input().split())
for i in range(n):
    x, y = map(float, input().split())
    p[i] = Point(x, y)
v = Convex_hull(p, n, ch)    # 返回凸包的顶点数v
ans = 0
for i in range(v):  ans += Distance(ch[i], ch[(i+1)%v])  # 计算凸包周长
ans += 2.0 * math.pi * r  # 加上一个圆周长
print(round(ans))    # 四舍五入取整

你可能感兴趣的:(算法竞赛快冲300题,算法)