C# 自嗨:2019年安徽省程序设计大赛题目——现代艺术

原题链接: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;
            }
        }
    }
}

运行结果

普通矩形:
在这里插入图片描述
正方形:
在这里插入图片描述
等腰三角形:
在这里插入图片描述
普通直角三角形:
在这里插入图片描述
朝右的铅笔头:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值