汉字田(第十五届蓝桥杯大赛软件赛国赛)
汉字田(第十五届蓝桥杯大赛软件赛国赛)
🧩 题目描述
在中国传统文化中,汉字“田”具有非常重要的地位。其最初的象形意义展现了一块被四等分的农田,四个等分代表农田的不同区域,中间交叉点则代表了田间的道路或水渠。在古代,农田的划分与耕作是社会经济的根基,所以“田”字不仅代表农田,还象征着土地、财产和生产力。到了现代,虽“田”字的直接象形意义已经不再明显,但它仍是生活中不可或缺的一部分,在语言文字以及数学逻辑思维训练中都发挥着作用。比如在几何问题中,“田字格”常常被用来帮助理解空间问题。
现在,若将汉字“田”抽象为一个由9个点组成的图形,这些点分布在田字格的四个角、四条外边的中点以及田字格的中心。那么请问,一共有多少条直线恰好只经过这9个点中的任意两个点?
🧠 解题思路
-
第一步:建模与抽象。
将题目中的图形或结构进行简化与抽象,例如将“田”字转化为一个 3×3 的点阵,明确点的坐标分布,便于后续几何计算或关系判断。 -
第二步:枚举与去重。
枚举所有满足条件的元素组合(如两点构成一条直线),通过一定的数据结构(如集合、哈希)去重,统计符合要求的不同结果数量。 -
关键点:
- 把所有点坐标统一建模,便于计算。
- 明确“恰好经过两点”的含义,排除与其他点共线但未计入的情况。
- 使用数学方式表示直线(如斜率 + 截距、方向向量 + 起点)以便唯一标识。
- 注意重复直线的判断,保证每条有效直线只统计一次。
💻 代码实现(语言:Java)
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改public class Main {static class Point {int x, y;Point(int x, int y) { this.x = x; this.y = y; }}static class Line {// 用斜率和截距(或方向向量 + 起点)唯一标识一条直线int dx, dy;int x, y;Line(Point a, Point b) {dx = b.x - a.x;dy = b.y - a.y;int g = gcd(dx, dy);if (g != 0) {dx /= g;dy /= g;}if (dx < 0 || (dx == 0 && dy < 0)) {dx = -dx;dy = -dy;}if (a.x < b.x || (a.x == b.x && a.y <= b.y)) {x = a.x;y = a.y;} else {x = b.x;y = b.y;}}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof Line)) return false;Line other = (Line) obj;return dx == other.dx && dy == other.dy && x == other.x && y == other.y;}@Overridepublic int hashCode() {return Objects.hash(dx, dy, x, y);}private int gcd(int a, int b) {return b == 0 ? Math.abs(a) : gcd(b, a % b);}}public static void main(String[] args) {List<Point> points = new ArrayList<>();// 3x3田字格的点坐标:(0,0)-(2,2)for (int x = 0; x <= 2; x++) {for (int y = 0; y <= 2; y++) {points.add(new Point(x, y));}}Set<Line> result = new HashSet<>();for (int i = 0; i < points.size(); i++) {for (int j = i + 1; j < points.size(); j++) {Point a = points.get(i), b = points.get(j);boolean onlyTwo = true;for (int k = 0; k < points.size(); k++) {if (k == i || k == j) continue;if (isColinear(a, b, points.get(k))) {onlyTwo = false;break;}}if (onlyTwo) {result.add(new Line(a, b));}}}System.out.println(result.size());}// 判断三点是否共线static boolean isColinear(Point a, Point b, Point c) {return (b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y);}
}
💻 代码实现(语言:Python)
from math import gcdclass Point:def __init__(self, x, y):self.x = xself.y = yclass Line:def __init__(self, a: Point, b: Point):dx = b.x - a.xdy = b.y - a.yg = gcd(dx, dy)if g != 0:dx //= gdy //= g# 规范方向,保证唯一性if dx < 0 or (dx == 0 and dy < 0):dx = -dxdy = -dy# 起点选较小的那个点,保证唯一性if (a.x < b.x) or (a.x == b.x and a.y <= b.y):self.x, self.y = a.x, a.yelse:self.x, self.y = b.x, b.yself.dx = dxself.dy = dydef __eq__(self, other):return (self.dx, self.dy, self.x, self.y) == (other.dx, other.dy, other.x, other.y)def __hash__(self):return hash((self.dx, self.dy, self.x, self.y))def is_colinear(a: Point, b: Point, c: Point) -> bool:return (b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y)def main():points = [Point(x, y) for x in range(3) for y in range(3)]lines = set()n = len(points)for i in range(n):for j in range(i + 1, n):a = points[i]b = points[j]# 判断是否恰好只有这两个点共线only_two = Truefor k in range(n):if k == i or k == j:continuec = points[k]if is_colinear(a, b, c):only_two = Falsebreakif only_two:lines.add(Line(a, b))print(f"{len(lines)}")if __name__ == "__main__":main()
每天带你写一道程序设计题,讲思路,写代码,练算法。
(编程题来源于网络,代码与讲解为本人原创。)