P8720 [蓝桥杯 2020 省 B2] 平面切分
题目描述
平面上有 N 条直线, 其中第 i 条直线是 y=Ai⋅x+Bi 。
请计算这些直线将平面分成了几个部分。
输入格式
第一行包含一个整数 N。
以下 N 行, 每行包含两个整数 Ai,Bi。
输出格式
一个整数代表答案。
输入输出样例
输入 #1复制
3 1 1 2 2 3 3
输出 #1复制
6
说明/提示
对于 50% 的评测用例, 1≤N≤4,−10≤Ai,Bi≤10。
对于所有评测用例, 1≤N≤1000,−10^5≤Ai,Bi≤10^5。
蓝桥杯 2020 第二轮省赛 B 组 I 题
解题思路:
这题我们首先需要定义一个包含两个元素的对象typedef pair<int int >PII;和typedef pair<double double>PDD;,这两个PII一个用来存储每次输入斜线的斜率k和截距b,PDD则是用来存储新添加的线的焦点坐标线x,y, 其次就是定义set<PII>lines,进行出入将输入的的k,b通过lines.insert(k,b)进行存储,“这里利用set容器的去重判断是否有不重复的线加入,不重复的线必定相交”,这就进入到Get_point 函数中进行
// 函数:计算当前直线与已存在的直线交点的个数 int get_point(int k, int b) { set<PDD> point; // 用于存储当前直线与其他直线的交点 (x, y) // 遍历所有已存在的直线 for (it = lines.begin(); it != lines.end(); it++) { int K = it->first; // 已存在直线的斜率 int B = it->second; // 已存在直线的截距 // 如果两条直线的斜率不同,则计算交点 if (K != k) { double x = 1.0 * (B - b) / (k - K); // 计算交点的 x 坐标 double y = 1.0 * k * x + b; // 计算交点的 y 坐标 point.insert({x, y}); // 将交点加入集合,避免重复 } } // 返回交点的数量 return point.size(); }
如果有新的交点最终的平面被划分为:新的交点个数+1;依次累加
C++ STL(标准模板库)中的
set
容器与迭代器 的使用。以下是对其中涉及到的知识点:1.
typedef
的使用
typedef
是 C++ 中的一个关键字,用于给现有的数据类型定义一个新的名字(别名)。这在代码中非常常见,尤其是在类型较长或者复杂时,使用typedef
可以提高代码的可读性和简洁性。示例:
typedef pair<double, double> PDD;
pair<double, double>
:是 C++ STL 中的一个模板类,它用来存储一对值,通常用于表示关联数据,如(x, y)
坐标或(key, value)
键值对。
double
类型表示浮点数,pair<double, double>
存储的是两个浮点数。
pair
是一个模板类,它可以接受任意类型作为参数,表示一对值。pair<double, double>
表示存储两个double
类型的值。
typedef pair<double, double> PDD;
:给pair<double, double>
这个类型起了一个别名,叫做PDD
。以后我们可以直接使用PDD
来代替pair<double, double>
,使代码更加简洁和易于理解。例子:
PDD point = {3.5, 4.2}; // 创建一个 PDD 类型的变量,表示点 (3.5, 4.2) cout << point.first << ", " << point.second << endl; // 输出 3.5, 4.2
示例:
typedef pair<int, int> PII;
pair<int, int>
:同样是 C++ STL 中的一个pair
类型,表示一个包含两个int
类型元素的对象。这里的int
类型通常用于表示整数,比如直线的斜率k
和截距b
。
typedef pair<int, int> PII;
:给pair<int, int>
这个类型起了一个别名,叫做PII
。以后我们可以直接使用PII
来代替pair<int, int>
,简化代码。例子:
PII line = {2, 3}; // 创建一个 PII 类型的变量,表示直线 y = 2x + 3 cout << line.first << ", " << line.second << endl; // 输出 2, 3
2.
set
容器
set
是 C++ STL(标准模板库)中的一个关联容器,它存储唯一的元素,并且这些元素会按照某种排序规则(默认是升序)自动排序。set
容器不允许有重复元素。
基本特性:
自动排序:
set
内部的元素会根据operator<
(默认)或者自定义的比较规则进行自动排序。元素唯一性:
set
中的元素不能重复,若尝试插入重复的元素,插入操作将会失败。用法:在这段代码中,
set<PII>
用来存储一组直线的斜率和截距,PII
是pair<int, int>
类型,即每个元素是一个表示直线的(k, b)
对。set<PII> lines; // 声明一个 set 容器,用于存储 (k, b) 形式的直线
通过
insert
方法将新的直线加入set
中:lines.insert({2, 3}); // 插入直线 y = 2x + 3 lines.insert({1, 1}); // 插入直线 y = x + 1
插入时,
set
会自动对直线的(k, b)
进行排序,按照斜率k
和截距b
的升序排列。由于
set
中的元素是唯一的,如果你尝试插入一个重复的(k, b)
,插入操作将会失败(即元素不会被加入)。3. 迭代器:
set<PII>::iterator it;
iterator
是一种遍历 C++ STL 容器的对象,用于访问容器中的元素。
set<PII>::iterator it
声明了一个迭代器it
,用于遍历set<PII>
容器中的元素。这里,it
会指向set
中每一个存储的PII
类型的元素(即pair<int, int>
)。迭代器的常见用法是通过
it
来访问容器中的元素,通常使用begin()
获取指向容器第一个元素的迭代器,使用end()
获取指向容器最后一个元素之后的位置的迭代器。例如:
for (it = lines.begin(); it != lines.end(); ++it) { cout << it->first << ", " << it->second << endl; // 输出每条直线的 k 和 b }
it->first
表示当前直线的斜率k
。
it->second
表示当前直线的截距b
。总结
typedef pair<double, double> PDD;
和typedef pair<int, int> PII;
通过typedef
给pair
类型起了别名,使得代码更简洁,方便表示坐标和直线的斜率与截距。
set<PII>
是一个set
容器,存储PII
类型的元素,每个元素表示一条直线的斜率和截距。set
容器保证元素的唯一性,并且会自动按升序排列。
set<PII>::iterator it
是用于遍历set<PII>
容器的迭代器,能够按顺序访问每条直线的斜率和截距。这些概念和容器在实际编程中非常常见,能够帮助我们高效地处理和存储一组数据,尤其是需要自动排序且不允许重复的场景。
#include<bits/stdc++.h>
using namespace std;
// 宏定义和类型别名
#define rep(x,y,z),for(int x=y;x<=z;x++) // 用于简化循环结构
typedef pair<double, double> PDD; // 用于表示交点的坐标对 (x, y)
typedef pair<int, int> PII; // 用于表示直线的斜率 (k) 和截距 (b)
set<PII> lines; // 用于存储平面上的所有直线,每个元素为一条直线的 (k, b)
set<PII>::iterator it; // 用于遍历 'lines' 集合
int ans = 1; // 初始区域数,平面没有直线时为 1
int n, k, b; // n 表示直线数量,k 和 b 分别表示当前直线的斜率和截距
// 函数:计算当前直线与已存在的直线交点的个数
int get_point(int k, int b) {
set<PDD> point; // 用于存储当前直线与其他直线的交点 (x, y)
// 遍历所有已存在的直线
for (it = lines.begin(); it != lines.end(); it++) {
int K = it->first; // 已存在直线的斜率
int B = it->second; // 已存在直线的截距
// 如果两条直线的斜率不同,则计算交点
if (K != k) {
double x = 1.0 * (B - b) / (k - K); // 计算交点的 x 坐标
double y = 1.0 * k * x + b; // 计算交点的 y 坐标
point.insert({x, y}); // 将交点加入集合,避免重复
}
}
// 返回交点的数量
return point.size();
}
int main() {
cin >> n; // 输入直线的数量
// 循环处理每一条直线
while (n--) {
cin >> k >> b; // 输入当前直线的斜率 k 和截距 b
int cnt = lines.size(); // 获取当前已存在的直线数
lines.insert({k, b}); // 将当前直线加入到集合中
// 如果直线集合的大小发生变化,说明当前直线是新加入的
if (cnt != lines.size()) {
// 更新区域数:新增的直线会通过交点增加区域数
ans += get_point(k, b) + 1; // 增加的区域数 = 交点数 + 1
}
}
// 输出最终的区域数
cout << ans;
return 0;
}