AI识别面板指示灯排序方法:先行后列规则详解与示例
题目描述

AI 察觉到面板上存在着 N 个指示灯,这里的 N 满足 1 小于等于 N 且 N 小于等于 100。这些指示灯的大小是相同的,并且任意两个指示灯之间不存在重叠的情况。
因为 AI 存在识别误差,每次识别到的指示灯位置或许会有所不同,会用 4 个坐标值来描述 AI 所识别的指示灯的大小和位置,分别是左上角的 x1、y1 以及右下角的 x2、y2。
[id_759590875]
每次从尚未排序的灯中挑选出最高的灯作为基准灯,接着找出与该基准灯属于同一行的所有灯并对其进行排序。当两个灯的高低偏差不超过灯半径时,就判定这两个灯属于同一行,也就是两个灯坐标的差小于或等于灯高度的一半。输入描述
第一行为N,表示灯的个数
接下来N行,每行为1个灯的坐标信息,格式为:
编号 x1 y1 2 y2
输出描述
排序后的编号列表,编号之间以空格分隔
示例1
输入
5
1 0 0 2 2
2 6 1 8 3
3 3 2 5 4
5 5 4 7 6
4 0 4 2 6
输出
1 2 3 4 5
解题思路
题目要求对 AI 识别到的多个指示灯编号进行排序,排序需遵循以下规则:
识别灯的位置:每个指示灯的坐标信息包含了左上角的坐标(x1, y1)以及右下角的坐标(x2, y2),这就构成了灯的边界矩形。灯的位置是由左上角的 y1 高度来定义的,y1 越小,灯就越靠上。排序的规则如下:排序需按行顺序进行。具体方法为:先找出尚未排序的灯中最高的灯,将其作为基准灯,接着找出与该基准灯处于同一行的所有灯。输出格式:依照上述排序后的编号顺序把编号输出出来,编号之间以空格进行分隔。例如进行解析。
给定输入:
5
1 0 0 2 2
2 6 1 8 3
3 3 2 5 4
5 5 4 7 6
4 0 4 2 6
按照题意分析和排序步骤如下:
最终输出结果为:
1 2 3 4 5
Java
引入 Java 中的优先队列类 PriorityQueue 。
import java.util.Comparator;
import java.util.Scanner;
以下是改写后的句子:引入了 Java 中的用于字符串连接的类 StringJoiner 。
public class Main {
公共静态的 void 主函数(String 类型的数组 args)。 主函数用于程序的入口点,在其中可以执行各种操作和逻辑。它接收命令行参数 args 作为输入,并根据这些参数进行相应的处理。 通常在这个函数中会初始化程序的环境、创建对象、调用其他函数等操作,以启动整个程序的运行。
创建一个 Scanner 对象,名为 sc,它用于从 System.in 中获取输入。
获取输入的灯的数量,将输入的字符串通过 Integer.parseInt 方法转换为整数赋值给变量 n
PriorityQueue pqY = new PriorityQueue<>使用 Comparator.comparingInt 方法,该方法接受一个函数式接口参数 l -> l.y,通过这个函数式接口按 y 坐标进行排序
// 读取灯的数据并添加到优先队列
for (int i = 0; i < n; i++) {
String[] data = sc.nextLine().split(" "); // 使用 split 分割输入行
int id = Integer.parseInt(data[0]);
int x1 = Integer.parseInt(data[1]);
int y1 = Integer.parseInt(data[2]);
int x2 = Integer.parseInt(data[3]);
int y2 = Integer.parseInt(data[4]);
int cx = (x1 + x2) / 2; // 中心x坐标
int cy = (y1 + y2) / 2; // 中心y坐标
整数 rad 等于 x2 减去 x1 的结果再除以 2 ;这是半径的值 。
pqY.offer(new Light(id, cx, cy, rad));
}
StringJoiner result = new StringJoiner(" ");
PriorityQueue pqX = new PriorityQueue<>使用 Comparator 的 comparingInt 方法,传入一个 lambda 表达式 l -> l.x,用于按 x 坐标进行排序
while (!pqY.isEmpty()) {
当前基准灯的光反射值为 pqY.poll()。
pqX.offer(ref); // 添加基准灯到当前行
// 查找并添加同一行的灯
当 pqY 不为空且 pqY 队列头部元素的 y 坐标与 ref.y 的绝对值满足一定条件时<= ref.r) {
pqX.offer(pqY.poll());
}
// 输出当前行的灯编号
while (!pqX.isEmpty()) {
result.add(String.valueOf(pqX.poll().id)); // 按x坐标顺序输出同一行的灯编号
}
}
System.out.println(result.toString()); // 输出结果
}
}
class Light {
int id;
int x;
int y;
int r;
public Light(int id, int x, int y, int r) {
this.id = id;
this.x = x;
this.y = y;
this.r = r;
}
}
Python
import heapq
灯对象包含以下内容:id 、x 坐标、y 坐标以及半径 r 。
class Light:
初始化自身,包含 id、x、y、r 这些参数。
self.id = id
self.x = x
self.y = y
self.r = r
# 定义排序规则:按 y 坐标排序
def __lt__(self, other):
return self.y < other.y
def main():
n = int(input()) # 读取灯的数量
pqY = [] # 按 y 坐标排序的优先队列(最小堆)
# 读取灯的数据并添加到优先队列
for _ in range(n):
data = list(map(int, input().split())) # 读取一行并分割为整数列表
id, x1, y1, x2, y2 = data
cx = (x1 + x2) // 2 # 中心 x 坐标
cy = (y1 + y2) // 2 # 中心 y 坐标
rad = (x2 - x1) // 2 # 半径
heapq.heappush(pqY, Light(id, cx, cy, rad)) # 将灯对象加入到 y 坐标最小堆
result = [] # 存储最终输出的灯编号
pqX = [] # 按 x 坐标排序的最小堆
# 处理每一行灯
while pqY:
ref = heapq.heappop(pqY) # 弹出当前行的基准灯
heapq.heappush(pqX, ref) # 将基准灯添加到当前行
# 查找并添加同一行的灯
while pqY and abs(pqY[0].y - ref.y) <= ref.r:
heapq.heappush(pqX, heapq.heappop(pqY))
# 按 x 坐标顺序输出同一行的灯编号
while pqX:
result.append(str(heapq.heappop(pqX).id))
print(" ".join(result)) # 输出结果
main()
JavaScript
需要引入 readline 模块,其名称为 readline ,通过 require 函数来获取该模块的功能。
创建一个名为 rl 的接口,该接口是通过 readline 创建的。
input: process.stdin,
output: process.stdout
});
class Light {
constructor(id, x, y, r) {
this.id = id;
this.x = x;
this.y = y;
this.r = r;
}
}
// 优先队列排序函数,按 y 坐标排序
定义一个名为 yComparator 的函数,该函数接受两个参数 a 和 b,函数体内部是 a 的 y 属性减去 b 的 y 属性的计算式,用于比较两个对象的 y 属性大小。
// 按 x 坐标排序
定义一个名为 xComparator 的函数,该函数接收两个参数 a 和 b,函数内部通过计算 a 的 x 属性值减去 b 的 x 属性值来进行比较。
const main = async () => {
const input = [];
使用 await 从 rl 中逐行获取 line 。
input.push(line.trim());
}
获取输入的第一个元素并将其解析为十进制整数,赋值给变量 n,n 表示灯的数量。
const pqY 是一个用于按 y 坐标进行排序的灯的优先队列,它里面没有元素,初始为空数组。
// 读取每个灯的坐标
for (let i = 1; i <= n i const id x1 y1 x2 y2='input[i].split("' .mapnumber const cx='Math.floor((x1' x2 2 x const cy='Math.floor((y1' y2 2 y const rad='Math.floor((x2' - x1 2 pqy.pushnew lightid cx cy rad pqy.sortycomparator pqy y const result='[];' let pqx='[];' x while pqy.length> 0) {
const ref 等于 pqY 进行 shift 操作后的结果;这就是当前行的基准灯。
pqX.push(ref); // 将基准灯添加到当前行
pqX 按照 x 坐标进行排序;对 pqX 依据 x 坐标进行排序操作;通过 x 坐标对 pqX 进行排序处理;以 x 坐标为依据对 pqX 进行排序;按照 x 坐标的顺序对 pqX 进行排序;依据 x 坐标来对 pqX 进行排序;用 x 坐标对 pqX 实施排序;基于 x 坐标对 pqX 展开排序;以 x 坐标为准则对 pqX 进行排序
// 查找并添加同一行的灯
当 pqY 的长度大于 0 且 pqY 的第一个元素的 y 坐标与 ref.y 的绝对值<= ref.r pqx.pushpqy.shift pqx.sortxcomparator x while pqx.length> 0) {
首先弹出 pqX 的第一个元素,然后获取该元素的 id 属性并转换为字符串,最后将这个字符串添加到 result 数组中。
}
}
console.log(result.join(" ")); 这行代码用于输出最终的结果。
};
main();
C++
#include
#include
#include
#include
#include
#include
using namespace std;
灯对象结构体,其中包含有 id ,还有 x 坐标,以及 y 坐标,同时包含半径 r 。
struct Light {
int id, x, y, r;
定义一个名为 Light 的类,该类接受四个参数:id、x、y 和 r。在类的构造函数中,将传入的参数 id 赋值给成员变量 id,将参数 x 赋值给成员变量 x,将参数 y 赋值给成员变量 y,将参数 r 赋值给成员变量 r。
};
// y 坐标排序的比较器
struct CompareY {
定义一个操作符函数,用于比较两个 Light 类型的对象 a 和 b。如果满足特定条件,则返回 true;否则返回 false。
return a.y > b.y;
}
};
// x 坐标排序的比较器
struct CompareX {
bool operator()(const Light& a, const Light& b) {
return a.x > b.x;
}
};
int main() {
int n;
cin >> n; // 读取灯的数量
priority_queue按 y 坐标排序的优先队列是 CompareY 且 pqY 与之相关
// 读取灯的数据并添加到优先队列
for (int i = 0; i < n i int id x1 y1 x2 y2 cin>> id >> x1 >> y1 >> x2 >> y2;
这就是中心 x 坐标。
中心 y 坐标是通过将 y1 和 y2 相加后再除以 2 得到的,即 int cy = (y1 + y2) / 2;
int rad = (x2 - x1) / 2; // 半径
pqY 把灯对象加入到了 y 坐标优先队列中,这个灯对象带有 id、cx、cy 和 rad 这些属性。
}
stringstream result;
priority_queue按 x 坐标排序的优先队列是 CompareX> pqX;
// 处理每一行灯
while (!pqY.empty()) {
Light ref = pqY.top();
pqY.pop();
pqX.push(ref);
// 查找并添加同一行的灯
当 pqY 不为空且 pqY 顶部元素的 y 值与 ref.y 的绝对值小于等于 eps 时,执行循环体中的操作,然后弹出 pqY 的顶部元素。<= ref.r) {
pqX.push(pqY.top());
pqY.pop();
}
// 输出当前行的灯编号
while (!pqX.empty()) {
result << pqX.top().id << " ";
pqX.pop();
}
}
cout << result.str() << endl; // 输出最终结果
return 0;
}
C语言
#include
#include
#include
#include
假设最多能有 1000 盏灯;这里定义了 MAX_LIGHTS 为 1000 ;即最多有 1000 盏灯的意思;定义的 MAX_LIGHTS 表示最多的灯的数量为 1000 ;意味着最多有 1000 盏灯;MAX_LIGHTS 被定义为最多有 1000 盏灯的数量;定义的 MAX_LIGHTS 意味着最多可存在 1000 盏灯;最多有 1000 盏灯是通过定义 MAX_LIGHTS 来体现的;定义的 MAX_LIGHTS 是关于最多有 1000 盏灯的设定。
定义一个结构体来表示灯,这个结构体包含灯的 id 、x 坐标、y 坐标以及半径 r 。
typedef struct {
int id;
int x;
int y;
int r;
} Light;
// 按 y 坐标排序的比较函数,用于 qsort
定义一个比较函数 compareByY,它接受两个 const void* 类型的参数 a 和 b,用于比较两个元素在 y 轴上的大小关系。
首先获取 a 所指向的 Light 结构体中的 y 值,即 ((Light *)a)->y ;然后获取 b 所指向的 Light 结构体中的 y 值,即 ((Light *)b)->y ;最后用获取到的 a 的 y 值减去 b 的 y 值,即 ((Light *)a)->y - ((Light *)b)->y 。
}
// 按 x 坐标排序的比较函数,用于 qsort
定义一个比较函数 compareByX,它接受两个 const void* 类型的参数 a 和 b,用于比较它们的大小。
首先获取指向 a 的 Light 类型指针并解引用,然后获取其 x 属性值;接着获取指向 b 的 Light 类型指针并解引用,再获取其 x 属性值;最后用前一个 x 值减去后一个 x 值。
}
int main() {
int n; // 灯的数量
存储灯的数组为 Light lights[MAX_LIGHTS];
Light 类型的数组 LightRowLights,其大小为 MAX_LIGHTS,用于存储同一行的灯。
// 读取灯的数量
scanf("%d", &n);
// 读取每个灯的数据
for (int i = 0; i < n; i++) {
int id, x1, y1, x2, y2;
scanf("%d %d %d %d %d", &id, &x1, &y1, &x2, &y2);
int cx = (x1 + x2) / 2; // 计算中心 x 坐标
int cy = (y1 + y2) / 2; // 计算中心 y 坐标
int rad = (x2 - x1) / 2; // 计算半径 r
lights[i] = (Light){id, cx, cy, rad}; // 将灯信息存入数组
}
// 对灯数组按 y 坐标排序
qsort(lights, n, sizeof(Light), compareByY);
int result[MAX_LIGHTS]; // 存储结果灯的 ID 的数组
int resultIndex = 0; // 结果数组的索引
int i = 0;
while (i < n) {
int rowCount = 0; // 当前行灯的数量
Light ref = lights[i]; // 基准灯
rowLights[rowCount++] = ref; // 将基准灯添加到当前行
i++;
// 查找并添加同一行的灯(基于 y 坐标和半径范围判断)
while (i < n && abs(lights[i].y - ref.y) <= ref.r) {
rowLights[rowCount++] = lights[i];
i++;
}
// 对当前行的灯按 x 坐标排序
qsort(rowLights, rowCount, sizeof(Light), compareByX);
// 按顺序将当前行的灯编号添加到结果
for (int j = 0; j < rowCount; j++) {
result[resultIndex++] = rowLights[j].id;
}
}
// 输出所有灯的编号
for (int i = 0; i < resultIndex; i++) {
printf("%d ", result[i]);
}
printf("\n");
return 0;
}