原题链接:https://blog.csdn.net/weixin_43773540/article/details/105388870
运行时间限制:2 s
题目描述:
给出平面上 N 个点的坐标点集, 求这 N 个点有多少条整体对称轴。整体对称是指一条直线,对于每个点,都能找到点集的一个点与他关于这条直线对称。
输入
第一行是一个整数N,表示点的个数
接下来 N 行,每行两个整数 X, Y,表示点的坐标
2 <= N <= 1000
-10000 <= X, Y <= 10000
输出
输出点集的整体对称轴有多少条
样例输入
4
0 0
0 1
1 0
1 1
样例输出
4
1.思路
1.1对称轴的定义
①对称轴上的任意一点与对称点的距离相等;
②对称点所连线段被对称轴垂直平分。
1.2根据定义想象情况
对于对称图的点
1.点在对称轴外就必须有一点与之符合对称轴定义的点
2.点在对称轴上
2.设限
让所有点排列组合
两两进行连线
如果是单数则判断最后剩下的是不是在对称轴上
有符合下列情况的就累计一条对称轴
当有多条对称轴时,所有对称轴必定相交于一点
所以对称轴的斜率k种类 = 对称轴数,无需求直线定义公式【Y= k × X + d】中的偏移量d
3.代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
namespace test
{
class Program
{
static void Main(string[] args)
{
List<string> readLineStrings = new List<string> {// 方便测试不写输入及输入验证
"4",
"0 0",
"2 0",
"0 2",
"2 2",
"3 1",// 测试排除
};
int N = int.Parse(readLineStrings[0]);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
readLineStrings.RemoveAt(0);
Point[] points = new Point[readLineStrings.Count];// 输入的坐标点集合
int i = 0;
foreach (string str in readLineStrings)
{
string[] x_y = str.Split();
Point newPoint = new Point(double.Parse(x_y[0]), double.Parse(x_y[1]));
if (!points.Contains(newPoint))
points[i++] = newPoint;
}
List<Point[]> pointsPermutation = PermutationAndCombination<Point>.GetPermutation(points);// 获取所有排列组合
List<Point> symmetryAxises = new List<Point>();// 对称轴斜率种类
foreach (Point[] permutation in pointsPermutation)// 逐个遍历排列组合
{
List<Point> slopes = new List<Point>();// 斜率种类,第一种必定是两点相连的,如果有第二种必定是与对称轴重合
List<Point> midPoints = new List<Point>();// 中点数
Queue<Point> permutationQueue = new Queue<Point>(permutation);
bool hasFind = true;// 这个排列组合是否找到对称轴
while (permutationQueue.Count > 0)
{
Point p1 = permutationQueue.Dequeue();
Point midPoint = p1;// 中点坐标默认是p1
if (permutationQueue.Any())// 讨论斜率,确定最多只有2种斜率,而且如果有必定是相互垂直
{
Point p2 = permutationQueue.Dequeue();
midPoint = new Point((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);// 中点坐标
Point slope = new Point(p1.X - p2.X, p1.Y - p2.Y);// 当前斜率
if (!slopes.Exists((s) =>
{
if (slope.Y == 0)// 平行于X轴
return s.Y == 0;
else if (slope.X == 0)// 平行于Y轴
return s.X == 0;
else
return slope.Y * s.X == slope.X * s.Y;// 斜率相等:避免除以0,转换成乘法(下同)
}))
{
slopes.Add(slope);
if (slopes.Count > 2)// 连接线的斜率种类大于2,就可以判定这组合没有对称轴,减少不必要判断
{
hasFind = false;
break;
}
else if (
slopes.Count == 2 // 存在两种斜率时,两者应该是垂直关系
&&
(slopes.Last().Y * slopes.First().Y != -1 * slopes.Last().X * slopes.First().X // slope.Last().Y/ slope.Last().X * (slopes.First().Y/slopes.First().X) == -1
&& !(slopes.First().X==0 && slopes.Last().X == 0)
&& !(slopes.First().Y==0 && slopes.Last().Y == 0)
)
)
{
hasFind = false;
break;
}
}
}
if (!midPoints.Contains(midPoint))// 如果两中点重合则肯定是当前点在之前某连线之间
{
midPoints.Add(midPoint);
if (midPoints.Count > 1)
{
Point midSlope = new Point(midPoints.First().X - midPoints.Last().X, midPoints.First().Y - midPoints.Last().Y);// 首末两中点连线的斜率
if ( midSlope.Y * slopes.First().Y != -1 * midSlope.X * slopes.First().X) // 中点连线的斜率肯定与第一斜率垂直 slope.Y/slope.X + (slopes.First().Y/slopes.First().X) == -1
{
hasFind = false;
break;
}
}
}
}
if (hasFind)
{
Point symmetryAxis = new Point(permutation[0].X - permutation[1].X, permutation[0].Y - permutation[1].Y);
if (!symmetryAxises.Exists((s) =>
{
if (symmetryAxis.Y == 0)// 平行于X轴
return s.Y == 0;
else if (symmetryAxis.X == 0)// 平行于Y轴
return s.X == 0;
else
return symmetryAxis.Y * s.X == symmetryAxis.X * s.Y;// 斜率相等
}))
symmetryAxises.Add(symmetryAxis);
}
}
stopwatch.Stop();
foreach (Point point in points)
Console.Write("(" + point + ")\t");
Console.WriteLine("\nsymmetryAxisCount(" + stopwatch.ElapsedMilliseconds + "ms):" + symmetryAxises.Count);
// 下面为显示原图布局,不影响结果
double Y_max = points.Max((p) => { return p.Y; });
double Y_min = points.Min((p) => { return p.Y; });
double cell = 2;// 单位长度(方便显示就行,尽量小)
for (double j = Y_max; j >= Y_min; j--)
{
Console.Write(j+" ");
List<Point> sameYPoints = new List<Point>(points).FindAll((p) => { return p.Y == j; });// 同一纵坐标的所有点
List<double> X_list = new List<double>();
foreach (Point sameYPoint in sameYPoints)
X_list.Add(sameYPoint.X);
X_list.Sort();
Queue<double> X_queue = new Queue<double>(X_list);
for (int k = 0; X_queue.Any(); k++)
for (int l = 0; l < cell; l++)
if (l == cell - 1 && k == (int)X_queue.Peek())
{
X_queue.Dequeue();
Console.Write("*");
}
else
Console.Write(" ");
Console.WriteLine();
}
Console.Write("\n ");
for (double j = points.Min((p) => { return p.X; }); j <= points.Max((p) => { return p.X; }); j++)
{
for (int l = 0; l < cell - 1; l++)
Console.Write(" ");
Console.Write(j);
}
Console.ReadKey();
}
public static class PermutationAndCombination<T>
{
/// <summary>
/// 交换两个变量
/// </summary>
/// <param name="a">变量1</param>
/// <param name="b">变量2</param>
public static void Swap(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
/// <summary>
/// 递归算法求数组的组合(私有成员)
/// </summary>
/// <param name="list">返回的范型</param>
/// <param name="t">所求数组</param>
/// <param name="n">辅助变量</param>
/// <param name="m">辅助变量</param>
/// <param name="b">辅助数组</param>
/// <param name="M">辅助变量M</param>
private static void GetCombination(ref List<T[]> list, T[] t, int n, int m, int[] b, int M)
{
for (int i = n; i >= m; i--)
{
b[m - 1] = i - 1;
if (m > 1)
GetCombination(ref list, t, i - 1, m - 1, b, M);
else
{
if (list == null)
list = new List<T[]>();
T[] temp = new T[M];
for (int j = 0; j < b.Length; j++)
temp[j] = t[b[j]];
list.Add(temp);
}
}
}
/// <summary>
/// 递归算法求排列(私有成员)
/// </summary>
/// <param name="list">返回的列表</param>
/// <param name="t">所求数组</param>
/// <param name="startIndex">起始标号</param>
/// <param name="endIndex">结束标号</param>
private static void GetPermutation(ref List<T[]> list, T[] t, int startIndex, int endIndex)
{
if (startIndex == endIndex)
{
if (list == null)
list = new List<T[]>();
T[] temp = new T[t.Length];
t.CopyTo(temp, 0);
list.Add(temp);
}
else
for (int i = startIndex; i <= endIndex; i++)
{
Swap(ref t[startIndex], ref t[i]);
GetPermutation(ref list, t, startIndex + 1, endIndex);
Swap(ref t[startIndex], ref t[i]);
}
}
/// <summary>
/// 求从起始标号到结束标号的排列,其余元素不变
/// </summary>
/// <param name="t">所求数组</param>
/// <param name="startIndex">起始标号</param>
/// <param name="endIndex">结束标号</param>
/// <returns>从起始标号到结束标号排列的范型</returns>
public static List<T[]> GetPermutation(T[] t, int startIndex, int endIndex)
{
if (startIndex < 0 || endIndex > t.Length - 1)
return null;
List<T[]> list = new List<T[]>();
GetPermutation(ref list, t, startIndex, endIndex);
return list;
}
/// <summary>
/// 返回数组所有元素的全排列
/// </summary>
/// <param name="t">所求数组</param>
/// <returns>全排列的范型</returns>
public static List<T[]> GetPermutation(T[] t)
{
return GetPermutation(t, 0, t.Length - 1);
}
/// <summary>
/// 求数组中n个元素的排列
/// </summary>
/// <param name="t">所求数组</param>
/// <param name="n">元素个数</param>
/// <returns>数组中n个元素的排列</returns>
public static List<T[]> GetPermutation(T[] t, int n)
{
if (n > t.Length)
return null;
List<T[]> list = new List<T[]>();
List<T[]> c = GetCombination(t, n);
for (int i = 0; i < c.Count; i++)
{
List<T[]> l = new List<T[]>();
GetPermutation(ref l, c[i], 0, n - 1);
list.AddRange(l);
}
return list;
}
/// <summary>
/// 求数组中n个元素的组合
/// </summary>
/// <param name="t">所求数组</param>
/// <param name="n">元素个数</param>
/// <returns>数组中n个元素的组合的范型</returns>
public static List<T[]> GetCombination(T[] t, int n)
{
if (t.Length < n)
return null;
int[] temp = new int[n];
List<T[]> list = new List<T[]>();
GetCombination(ref list, t, t.Length, n, temp, n);
return list;
}
}
}
}
运行结果
普通矩形:
正方形:
等腰三角形:
普通直角三角形:
朝右的铅笔头: