《算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。
【题目描述】 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个圆,最外围的线条就是最短的围墙。
围墙的长度等于4根切线的长度,加上4个圆上的圆弧的长度。
容易观察得到,这4段圆弧合起来是一个完整的圆。所以,最小围墙的长度 = 凸包长度 + 一个圆周长度。
本题是凸包的裸题,直接套凸包的模板(模板代码见《算法竞赛》清华大学出版社,罗勇军,郭卫斌著,523页,“8.1.5 凸包”)。
【重点】 凸包。
#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;
}
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));
}
}
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)) # 四舍五入取整