第十二届蓝桥杯c++b组—直线

文章讲述了在平面直角坐标系中,通过计算不同点对确定的直线数量。通过使用map或set数据结构去重,考虑斜率和截距,以及平行于坐标轴的特殊情况,分别给出了两种C++实现的算法,一种使用map存储斜率和截距,另一种使用set处理直线类对象。最终目的是计算给定范围内所有点对确定的直线总数。

本题的关键思路:

  1. 去重,当k,b这个数对唯一时,直线的数目ans才能++,所以要么用map保证唯一解的个数,要么用set去重

  1. 求k ,b的公式 k=y1-y2/x1-x2; b=(x2*y1-x1*y2)/x1-x2;

  1. 平行于y轴的直线,k等于正无穷,所以要在答案上另外加上20,因为有20 列

题目:

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上, 那么这些点中任意两点确定的直线是同一条。

给定平面上 2 × 3 个整点(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z {(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z},即横坐标 是 0到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数 的点。这些点一共确定了 11 条不同的直线。

给定平面上 20×21 个整点 (x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},即横 坐标是 0到 19 (包含 0 和 19) 之间的整数、纵坐标是 0到 20 (包含 0和 20​) 之 间的整数的点。

请问这些点一共确定了多少条不同的直线。

//C++ unordered_map无法使用pair和vector作为key,只能用map使用pair作为键值使用
#include <utility>
#include<iostream>
#include<map>
#include<vector>
using namespace std;
struct Point {
    double x, y;
}p[25 * 25];//存下每一个点
map<pair<double, double>, int> mp;//存斜率k和截距b
int cnt, ans;
int main() {
    
    for (int i = 0; i < 20; i++) {
        for (int j = 0; j < 21; j++) {
            p[cnt].x = i;
            p[cnt++].y = j;
        }
    }
    for (int i = 0; i < cnt; i++) {
        for (int j = 0; j < cnt; j++) {
            //两点的直线与坐标轴平行或共点
            if (p[i].x == p[j].x || p[i].y == p[j].y) continue;
            //斜率和截距
            double k = (p[j].y - p[i].y) / (p[j].x - p[i].x);
            double b = (p[j].x * p[i].y - p[j].y * p[i].x) / (p[j].x - p[i].x);
        
            if (mp[{k, b}] == 0) {
                mp[{k, b}] = 1;
                ans++;
            }
        }
    }
    cout << ans+20+21 << endl;
    return 0;
}

第二种思路:

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <math.h>
#include <set>
using namespace std;
class Line {  //直线的K个B,判断直线是不是同一条,用set来查重。
public:
    double k, b;
    Line(double x, double y) {
        this->k = x;
        this->b = y;
    }
};
bool equals(double a, double b) { //改写等于号的规则,我们认为当两个数差的不是特别大,那么就是同一个数。
    if (fabs(a - b) <= 1e-8)
        return true;
    return false;
}
bool operator<(const Line& Line1, const Line& Line2) {  //如果false就不插入树,如果true就插入树中。//改写set的比较函数。注意参数必须是const &。
    if (equals(Line1.k, Line2.k)) {//如果k是相等的,就按照b的顺序插入树
        if (equals(Line1.b, Line2.b))//如果k和b都是相等的,就是不能插入,false。找不到一个位置让这个Line插入这颗树上。
            return false;
        return Line1.b < Line2.b;
    }
    if (equals(Line1.k, Line2.k))//如果k是相同的,就false。
        return false;
    return Line1.k < Line2.k;//如果k不相同,就继续比较插入。你可能会好奇,上一行的if是多余的,其实不是的,这是因为为了想要让两个相差很微小的k,先return。要不然的话,进入Line1.k<Line2.k哪怕两个k非常非常接近(1e-8)也会进行判断。
}
int main() {
    set<Line> Ssq;
    for (int x1 = 0; x1 < 20; x1++) {
        for (int y1 = 0; y1 < 21; y1++) {
            for (int x2 = 0; x2 < 20; x2++) {
                for (int y2 = 0; y2 < 21; y2++) {
                    if (x1 == x2 && y1 == y2) //同一个点没什么好搜的。
                        continue;
                    if (x1 != x2)  //避免斜率不存在的情况
                    {
                        double k = (double) (y2 - y1) / (x2 - x1);
                        double b = y2 - k * x2;
                        Ssq.insert(Line(k, b));
                    }
                }
            }
        }
    }
    cout << Ssq.size() + 20;//没有计算过x1==x2的直线,手动加上20来计算。
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值