【算法】【几何】

15 阅读3分钟

atcoder.jp/contests/ab…

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
struct P{
	ll px,py;
};
// 向量加法
P operator+(const P& a1, const P& a2) {
	return P{ a1.px + a2.px, a1.py + a2.py };
}

// 向量减法
P operator-(const P& a1, const P& a2) {
	return { a1.px - a2.px, a1.py - a2.py };
}

bool operator<(const P& a1, const P& a2) {
	if (a1.px < a2.px) return true;
	if (a1.px > a2.px) return false;
	if (a1.py < a2.py) return true;
	return false;
}long long crs(P p1, P p2) {
	return p1.px * p2.py - p1.py * p2.px;
}
int main(){
	int n;cin>>n;
	vector<P>pts(n);
	for(int i=0;i<n;i++){cin>>pts[i].px>>pts[i].py;
	
	}ll s=0;
	for(int i=2;i<n;i++){
		s+=abs(crs(pts[i-1]-pts[0],pts[i]-pts[0]));
		
	}ll res=8e18;
	ll e=0;
	int q=1;
	for(int p=0;p<n;p++){
		while(4*e<s){
			e+=abs(crs(pts[q]-pts[p],pts[(q+1)%n]-pts[p]));
			q=(q+1)%n;
			res=min(res,abs(4*e-s));
		}e-=abs(crs(pts[p]-pts[q],pts[(p+1)%n]-pts[q]));
		res=min(res,abs(4*e-s));}
		cout<<res;}
	

图片.png

图片.png

1. 对象是凸多边形(点按顺序排列)

2. 固定一个端点 p,另一个端点 q 往右移

3. 面积随 q 右移单调递增(只增不减)

4. 我们要找面积最接近某个目标值(S/4)

满足单调性 → 双指针不会回退 → 滑动窗口成立。

atcoder.jp/contests/ty…

找几个点的最大角

#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;

// 点结构体,存储坐标
struct Point {
	double px, py;
};

// 向量减法:a1 - a2
Point operator-(const Point& a1, const Point& a2) {
	return Point{ a1.px - a2.px, a1.py - a2.py };
}

// 计算向量 G 的极角(返回角度 0~360 度)
double getangle(Point G) {
	// 向量在 x 轴上方(y ≥ 0)
	if (G.py >= 0.0) {
		double len = sqrt(G.px * G.px + G.py * G.py);
		double cos_val = G.px / len;
		// acos 算出弧度 → 转角度
		double angle = acos(cos_val) * 180.0 / acos(-1.0);
		return angle;
	}
	// 向量在 x 轴下方(y < 0)
	else {
		double len = sqrt(G.px * G.px + G.py * G.py);
		double cos_val = G.px / len;
		double angle = acos(cos_val) * 180.0 / acos(-1.0);
		// 下方角度 = 360 - 上半平面角度
		return 360.0 - angle;
	}
}

// 计算两个极角 a1、a2 之间形成的最小夹角(0~180 度)
double getangle2(double a1, double a2) {
	double diff = abs(a1 - a2);
	// 取 <= 180° 的那个角
	if (diff >= 180.0) return 360.0 - diff;
	return diff;
}

int N;
Point G[2009];  // 存储所有点

// 以第 pos 个点为顶点,计算最大角
double solve(int pos) {
	// 把所有其他点相对于 pos 点的极角存起来
	vector<double> angles;
	for (int i = 1; i <= N; i++) {
		if (i == pos) continue;
		Point vec = G[i] - G[pos];
		double ang = getangle(vec);
		angles.push_back(ang);
	}

	// 极角排序(关键优化)
	sort(angles.begin(), angles.end());

	double max_angle = 0.0;

	// 对每个点 A,找与它夹角最接近 180° 的点 C
	for (int i = 0; i < angles.size(); i++) {
		// 目标角度 = 当前角度 + 180°
		double target = angles[i] + 180.0;
		if (target >= 360.0) target -= 360.0;

		// 二分查找最接近 target 的位置
		int pos1 = lower_bound(angles.begin(), angles.end(), target) - angles.begin();

		// 候选点:找到的点、前一个点(最接近180°)
		int c1 = pos1 % angles.size();
		int c2 = (pos1 - 1 + angles.size()) % angles.size();

		double ang1 = getangle2(angles[i], angles[c1]);
		double ang2 = getangle2(angles[i], angles[c2]);

		max_angle = max({ max_angle, ang1, ang2 });
	}
	return max_angle;
}

// 暴力 O(N^3) 解法(仅用来理解,N=2000 会超时)
double solve_Slow() {
	double ans = 0.0;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			for (int k = 1; k <= N; k++) {
				if (i == j || i == k || j == k) continue;
				double a1 = getangle(G[i] - G[j]);
				double a2 = getangle(G[k] - G[j]);
				ans = max(ans, getangle2(a1, a2));
			}
		}
	}
	return ans;
}

// 正解 O(N^2 log N) 算法
double solve_Fast() {
	double ans = 0.0;
	// 枚举每个点作为角的顶点
	for (int i = 1; i <= N; i++) {
		double current_max = solve(i);
		ans = max(ans, current_max);
	}
	return ans;
}

int main() {
	// 输入
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> G[i].px >> G[i].py;
	}

	// 计算答案
	double ans = solve_Fast();

	// 输出 12 位小数
	printf("%.12lf\n", ans);
	return 0;
}