计算几何题单

这篇博客汇总了多个计算几何题目,包括判断两点间曲线穿过多圆次数、凸多边形对角线交点计数、寻找平面上最接近点对、直线穿过点的数量以及正方形内点的特定划分问题。通过分析思路,探讨了解题方法和关键定理的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P1652 圆

大意:

给出 n 个圆,保证任意两个圆都不相交。
然后给出两个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x1,y1),(x2,y2) (x1,y1),(x2,y2),保证均不在某个圆上,要从 ( x 1 , y 1 ) → ( x 2 , y 2 ) (x1,y1)→(x2,y2) (x1,y1)(x2,y2)画条曲线,问这条曲线最少穿过多少次圆的边界?

思路:

因为是画曲线,而且任意两个圆都不相交,所以只要两个点不是分别在不同的圆里面,都有办法找到一条曲线绕过所有的圆,所以只需要判断,对于每个圆,是否两个点分别属于圆内和圆外即可

#include<bits/stdc++.h>
using namespace std;
double dis(int x1,int y1,int x2,int y2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main(){
    int x1,y1,x2,y2,ans=0;
    int y[105][3],n;
    cin>>n;
    for(int i=0;i<3;i++) 
        for(int j=0;j<n;j++)
            cin>>y[j][i];
    cin>>x1>>y1>>x2>>y2;
    for(int i=0;i<n;i++)
        if(dis(x1,y1,y[i][0],y[i][1])<y[i][2]!=dis(x2,y2,y[i][0],y[i][1])<y[i][2]) ans++; 
    cout<<ans<<endl;  
    return 0;
}

P2181 对角线

大意:

对于一个 nnn 个顶点的凸多边形,它的任何三条对角线都不会交于一点。请求出图形中对角线交点的个数。

思路:

找规律题…

#include<bits/stdc++.h>
long long a1[100000];
long long a2[100000];
long long a3[100000];
int main()
{
	long long n;
	scanf("%lld",&n);
	int t=0;
	for(int i=1;i<=n;i++)
	{
		a1[i]=a1[i-1]+i;
	}
	for(int i=1;i<=n;i++)
	{
		a2[i]=a2[i-1]+a1[i];
	}
	for(int i=1;i<=n;i++)
	{
		a3[i]=a3[i-1]+a2[i];
	}
	printf("%lld",a3[n-3]);
	return 0;
}

P1257 平面上的最接近点对

大意:

给定平面上 nnn 个点,找出其中的一对点的距离,使得在这 nnn 个点的所有点对中,该距离为所有点对中最小的。

思路:

裸题,直接分治求

#include<bits/stdc++.h>

using namespace std;

const int N = 1e4 + 5;
int n;
const double INF = 0x3f3f3f3f;
struct node
{
    double x, y;
}a[N];

bool cmpx(node a,node b){
    return a.x < b.x;
}

bool cmpy(node a,node b){
    return a.y < b.y;
}

double dis(node a,node b){
    return (sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
}

double solve(int l,int r){
    if(l==r){
        return INF;    //当前区间只有一个点,返回INF,避免更新答案
    }
    int mid = l + r >> 1;   
    double midnum = a[mid].x;   //区间中点
    double res = min(solve(l, mid), solve(mid + 1, r));  //分治求两边区间的最小距离
    sort(a + l, a + r + 1, cmpy);      //按照y轴排序,这样后面在找的时候只需要找最近的几个点即可
    int temp[N], cnt = 0;
    for (int i = l; i <= r; i++){
        if(a[i].x>=midnum-res&&a[i].x<=midnum+res){
            temp[cnt++] = i;     //将“中间地带”的点加入temp,准备判断
        }
    }
    for (int i = 0; i < cnt;i++){
        for (int j = i - 1; j >= 0 && (a[temp[i]].y - a[temp[j]].y < res) ;j--)  //因为已经排好序了,所以只会遍历到最近的几个点,可以证明最多只有6个点,所以复杂度为O(n)
        {
            res = min(res, dis(a[temp[i]], a[temp[j]]));
        }
    }
    return res;
}

int main(){
    cin>>n;
    for (int i = 0; i < n;i++){
        cin >> a[i].x >> a[i].y;
    }
    sort(a, a + n, cmpx);
    printf("%.4lf\n",solve(0, n - 1));
    return 0;
}

P1142 轰炸

大意:

给出n个点,问一条直线最多可以穿过多少个点 ( 1 ≤ n ≤ 700 ) (1\leq n \leq 700) (1n700)

思路:

n很小,直接暴力一波就行,先选出两个点,确定直线,然后遍历一遍看有多少点在这个直线上(这里因为用了一堆没啥用的结构体,需要开一下O2优化,不过无所谓了,领悟思路即可~)

#include<bits/stdc++.h>

using namespace std;

const int N = 700 + 5;
typedef long long LL;
int n;
const double eps = 1e-6;
// 精度三态函数(>0, <0, =0)
int dcmp(double x) {
    if (fabs(x) < eps)
        return 0;  // 如果差值非常小,那么返回0
    else if (x < 0)
        return -1;
    else
        return 1;
}
// 定义向量
struct Point {
    double x, y;
    Point() {}
    Point(double _x, double _y) : x(_x), y(_y) {}

    // 向量加减
    Point operator+(const Point a) const { return Point(x + a.x, y + a.y); }
    Point operator-(const Point a) const { return Point(x - a.x, y - a.y); }

    // 向量比较:等于、小于
    bool operator==(const Point a) const {
        return dcmp(x - a.x) == 0 && dcmp(y - a.y) == 0;
    }
    bool operator<(const Point a) const {
        if (x != a.x)  // 先比较x,然后比较y
            return x < a.x;
        else
            return y < a.y;
    }

    // 叉乘、点乘
    double operator*(const Point a) const {
        return x * a.y - y * a.x;
    }  // 叉乘(外积)
    double operator^(const Point a) const {
        return x * a.x + y * a.y;
    }  // 点乘(内积)
};
typedef Point Vector;

Vector a[N];

int main(){
    cin >> n;
    for (int i = 0; i < n;i++){
        cin >> a[i].x >> a[i].y;
    }
    int res = 0;
    for (int i = 0; i < n - 1;i++){
        for (int k = i+1; k < n;k++){
            int cnt = 2;
            for (int j = 0; j < n; j++){
                if(i==j||k==j)
                    continue;
                Point v1 = a[i] - a[k], v2 = a[k] - a[j];
                if (v1*v2 == 0){
                    cnt++;
                }
            }
            res = max(res, cnt);
        }     
    }
    cout << res << endl;
    return 0;
}

P7042 「MCOI-03」正方

大意:

给出abcd四个数,要求在一个正方形内选一个点,向四个顶点连线,分成的三个三角形面积比为 a : b : c : d a:b:c:d a:b:c:d,问有多少种方法

思路:

感觉这完全不是计算几何题啊…

可以发现只可能有 0 1 4 8的情况,因为分成四个三角形,所以将abcd从小到大排序的话,必须满足a+d=b+c,所以只要不符合的都是0,a=b=c=d的都是1,如果a=b则是4,其他情况都是8

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef unsigned long long ULL;
ULL n, a[5];
int main(){
    cin >> n;
    while(n--){
        for (int i = 0; i < 4;i++){
            cin >> a[i];
        }
        sort(a, a + 4);
        if (a[0]+a[3]!=a[1]+a[2]){
            cout << 0 << endl;
        }
        else if(a[0]==a[3]){
            cout << 1 << endl;
        }
        else if(a[0]==a[1]){
            cout << 4 << endl;
        }
        else{
            cout << 8 << endl;
        }
    }
    return 0;
}

P2735 [USACO3.4]网 Electric Fences

大意:

( 0 , 0 ) , ( n , m ) ( p , 0 ) (0,0),(n,m)(p,0) (0,0),(n,m)(p,0)组成的三角形,内部的点格点有多少

思路:

1定理:pick定理 pick定理是指一个计算点阵中顶点在格点上的多边形面积公式,该公式可以表示为2S=2a+b-2,其中a表示多边形内部的点数,b表示多边形边界上的点数,s表示多边形的面积。

2定理:(0,0)到(n,m)的线段上点的个数=gcd(n,m)+1, 定理2就是扩展欧几里得的特殊情况

ps.逆用pick定理时一定不能用(2s-b+2)/2,应该用s-b/2+1

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
LL n, m,p,res,s,b;
int main(){
    cin>>n>>m>>p;
    s=(p*m)/2;
    b = __gcd(n, m) + __gcd(abs(n - p), m) + p;
    cout << s - b / 2 + 1 << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值