【 蓝桥杯 C++】 2024年第十五届决赛真题-蚂蚁开会

题目描述

二维平面上有 n 只蚂蚁,每只蚂蚁有一条线段作为活动范围,第 i 只蚂蚁的活动范围的两个端点为 (uix, uiy),(vix, viy)。现在蚂蚁们考虑在这些线段的交点处设置会议中心。为了尽可能节省经费,它们决定只在所有交点为整点的地方设置会议中心,请问需要设置多少个会议中心?

输入格式

输入共 n + 1 行。第一行为一个正整数 n。后面 n 行,每行 4 个由空格分开的整数表示 uix, uiy, vix, viy。

输出格式

输出共 1 行,一个整数表示答案。

样例输入

4
0 0 4 4
0 4 4 0
2 0 0 4
2 1 2 3

样例输出

2

提示

【样例说明】

所有线段之间共有 3 个不同的交点:(0, 4),(43,43),(2, 2), 其中整点有 2 个:(0, 4),(2, 2)。

【评测用例规模与约定】

对于 20% 的评测用例,保证 0 ≤ uix, uiy, vix, viy ≤ 100。对于 100% 的评测用例,保证 n ≤ 500,0 ≤ uix, uiy, vix, viy ≤ 10000,保证任意蚂蚁的活动范围不会退化成一个点,不保证任意两条线段之间交点数量有限。


代码简介

使用C++的 set 来存储符合条件的坐标,以防止坐标重复。

在计算时采用 克拉默法则 计算线性方程组的解。

注意
1.防止坐标重复
2.注意是线段而不是直线,交点坐标必须是在线段范围内
3.考虑两条线段重合的情况

代码示例

#include<iostream>
#include<set>
#include<optional>
using namespace std;
struct zuobiao{
    int x;
    int y;
    // 定义 operator< 用于比较
    bool operator<(const zuobiao& other) const {
        if (x != other.x) {
            return x < other.x;  // 优先比较 x
        }
        return y < other.y;      // x 相同则比较 y
    }
};
// 根据克拉默法则计算线性方程组的解
bool juzhen(int x1[], int x2[], int B[], zuobiao& result){
    //    初始化矩阵A
    int A[2][2]={0};
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            if(i==0){
                A[j][i]=x1[j];
            }
            else
                A[j][i]=x2[j];
        }
    }
    // 计算A的行列式    
    int a=A[0][0]*A[1][1]-A[0][1]*A[1][0];
//    cout<<"a= "<< a <<endl;
    //    行列式的值等于0,无交点
    if(a==0){
//        cout<<"无交点"<<endl;
        return false;
    }
    //    下面都是克拉默计算公式
    int tx1=B[0]*A[1][1]-B[1]*A[0][1];
//    cout <<"tx1= "<<tx1<<endl;
    int tx2=A[0][0]*B[1]-A[1][0]*B[0];
//    cout <<"tx2= "<<tx2<<endl;
    //    判断交点是否为整数
    if(tx1%a!=0||tx2%a!=0){
//        cout<<"交点坐标为小数"<<endl;
        return false;
    }
    int xx1=tx1/a;
    int xx2=tx2/a;
//    cout<< "交点坐标"<<xx1<<' '<<xx2<<endl;
    result = zuobiao{xx1, xx2};
    return true;
}
struct Result {
    int x1;
    int x2;
    int B;
};
// 根据坐标计算方程的一般形式Ax+By=C
Result fangcheng(int x1,int y1,int x2,int y2){
    int tX1=y1-y2;            // A
    int tX2=(x1-x2)*(-1);    // B
    int tC=tX1*x1+tX2*y1;    // C
    return {tX1,tX2,tC};
}
int main(){
    zuobiao result;
    set <zuobiao> xy;
    //    输入初始数据
    int n=0;
    cin>>n;
    int line[n][4]={0};
    for(int i=0;i<n;i++){
        for(int j=0;j<4;j++){
            scanf("%d",&line[i][j]);
        }
    }
    // 穷举所有线段组合    
    for(int i=0;i<n-1;i++){
        for(int j=i+1;j<n;j++){
            //  得到两个方程的未知数的系数和常数项            
            auto f1 = fangcheng(line[i][0],line[i][1],line[i][2],line[i][3]);
            auto f2 = fangcheng(line[j][0],line[j][1],line[j][2],line[j][3]);
            int X1[]={f1.x1,f2.x1};
            int X2[]={f1.x2,f2.x2};
            int B[]={f1.B,f2.B};
            // 判断两线段是否重合
            if((f1.x1*f2.x2==f2.x1*f1.x2)&&(f1.x2 * f2.B == f2.x2 * f1.B)){
                // 如果两线段重合,找出重合部分的X坐标的范围
                int min_x=max(min(line[i][0],line[i][2]),min(line[j][0],line[j][2]));
                int max_x=min(max(line[i][0],line[i][2]),max(line[j][0],line[j][2]));
                for(int k=min_x;k<=max_x;k++){
                    // 在这个范围里,如果坐标为整数,这放入xy里
                    if((f1.B-f1.x1*k)%f1.x2==0){
                        int y=(f1.B-f1.x1*k)/f1.x2;
                        xy.insert(zuobiao{k,y});
                    }
                }
            }
            //     判断两线段是否有交点        
            else if(juzhen(X1,X2,B,result)){
                // 判断交点是否在线段范围内
                if((min(line[i][0],line[i][2])<=result.x && result.x<=max(line[i][0],line[i][2]))&&
                    (min(line[j][0],line[j][2])<=result.x && result.x<=max(line[j][0],line[j][2]))&&
                    (min(line[i][1],line[i][3])<=result.y && result.y<=max(line[i][1],line[i][3]))&&
                    (min(line[j][1],line[j][3])<=result.y && result.y<=max(line[j][1],line[j][3]))){
                    // 满足上述条件,加入xy
                    xy.insert(result);
//                printf("%d %d\n", result.x,result.y);
                }
//                result.insert(xy);
            }
        }
    }
//    for(auto it=xy.begin();it!=xy.end();it++){
//        printf("%d %d\n", it->x,it->y);
//    }
    // 输出xy里符合的坐标个数
    cout<<xy.size()<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值