【空间结构编程真题】已知平面上有m个点,求同一条直线所能通过的最多点数

方法一(子问题1):

【思路】

固定任意两端点,确定一条直线,依次取出剩下的m-2个点,若点在直线上则计数加一。

【适合考场作答】

#include <stdio.h>
#include <stdlib.h>
#define maxSize 1000

typedef struct
{
	float x;
	float y;
}Point;


int main()
{
	int i, j, k, m;
	int count, max, maxi,maxj;
	FILE* fp;
	Point pset[maxSize];

	//读取数据
	if ((fp = fopen("data.txt", "r")) == NULL)
	{
		exit(0);//<stdlib.h>库函数
	}

	k = 0;
	while (fscanf(fp, "%d%d", &i, &j) != EOF)
	{
		pset[k].x = i;
		pset[k].y = j;
		k++;
	}

	m = k;//假设平面上有m个点
	max = 0;
	//计算任意两点构成的直线通过的点数,并返回最大值
	for(i = 0;i<m;++i)
		for (j = 0; j < m; ++j)
		{
			if (i != j)
			{
				count = 0;
				for (k = 0; k < m; ++k)
				{
					if (k != i && k != j)
					{
						if ((pset[i].x - pset[j].x)*(pset[j].y - pset[k].y) == (pset[j].x - pset[k].x)*(pset[i].y - pset[j].y))
						{
							count++;
						}
					}
				}

				if (count > max)
				{
					max = count;
					maxi = i;
					maxj = j;
				}
			}
		}

	printf("max points:%d\n", max+2);

	//找出这些相等的点
	for (k = 0; k < m; ++k)
	{
		//即使是maxi,maxj直线两端点,也会输出,因为他们的与自身相减斜率为0,两边等式依旧成立
		if ((pset[maxi].x - pset[maxj].x)*(pset[maxj].y - pset[k].y) == (pset[maxj].x - pset[k].x)*(pset[maxi].y - pset[maxj].y))
		{
			printf("the %dth point (%.2f,%.2f)\n", k+1,pset[k].x,pset[k].y);
		}
	}




	return 0;
}

【测试数据】

4 4
3 1
3 3
1 2
2 2
2 1
1 1
2 3
1 3
3 2

【测试结果】

方法二(子问题2,3):

【不错的思路】

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define maxSize 100

/*
程序思想:平面上有n个点,求出通过点数最多的直线。步骤如下:
1)找出平面上所有直线。任意两线段斜率相同的为一个类型,归并在一条
   直线上,线段数加一;
2)根据线段数与点数的关系,找出通过的点数最多的那条直线,输出点数。

补充知识:设同一条直线上的j个点,任意两个点构成一个组合,所有的组合
数,就是这j个点能组成的线段数。为j*(j-1)/2条线段数
*/

typedef struct
{
	double x;
	double y;
}Point;

typedef struct
{
	double x_axis;//x轴截距
	double y_axis;//y轴截距
	double k;//直线斜率
	int count;//线段数
}Line;

/*输入n个点,求出m条直线*/
int Get_Lines(Line line[], Point point[], int n)
{
	int i, j, t;//循环变量
	double k, x_axis, y_axis;//斜率,x轴截距,y轴截距
	int m = 0;//设有m条直线

	/*两个循环,求出了所有任意两个点的组合(穷举法),如
	  果,两个点构成的直线与之前两个点构成的直线相同,记作
	  该直线的一条新线段。对于j个点共线的一条直线,线段数
	  为j*(j-1)/2。
	*/
	for (i = 0; i < n - 1; i++)
	{
		for (j = i + 1; j < n; j++)
		{
			//Pi和Pj构成的直线垂直于y轴
			if (fabs(point[i].x - point[j].x) < 1e-5)
			{
				k = 1e5;//表示斜率无穷大
				x_axis = point[i].x;//x轴截距
				y_axis = 1e5;//y轴截距无穷大
			}
			else if(fabs(point[i].y - point[j].y) < 1e-5)
			{
				k = 0;
				x_axis = 1e5;
				y_axis = point[i].y;
			}
			else
			{
				k = (point[i].y - point[j].y) / (point[i].x - point[j].x);//以Pj为基准点求直线斜率
				y_axis = point[i].y - k * point[i].x;// y = kx + b,反过来求解出b,即y轴截距
				x_axis = -1 * y_axis / k;//k,即是tanα = (y_axis)/(-x_axis),纸上画个草图即可想起
			}

			for (t = 0; t < m; t++)
			{
				//判断新求出的直线是否已经存在
				if ((fabs(line[t].x_axis - x_axis) < 1e-5) && (fabs(line[t].y_axis - y_axis) < 1e-5) && ((fabs(line[t].k - k) < 1e-5)))
				{

					line[t].count++;//若新直线已经存在,线段数加1,Pi和Pj构成一个组合
					break;
				}
			}

			//说明0到m-1条直线都与新直线不同
			if (t == m)
			{
				line[m].x_axis = x_axis;
				line[m].y_axis = y_axis;
				line[m].k = k;
				line[m].count = 1;
				m++;
			}
		}
		
	}

	return m;
}


int main()
{
	Point point[maxSize];
	Line line[maxSize];
	FILE* fp;
	int i, j;
	int x, y;
	int m, n;

	fp = fopen("data.txt", "r");
	if((fp = fopen("data.txt", "r")) == NULL)
	{
		exit(0);
	}

	i = 0;
	while (fscanf(fp, "%d%d", &x, &y) != EOF)
	{
		point[i].x = x;
		point[i].y = y;
		++i;
	}

	n = i;
	m = Get_Lines(line, point, n);

	int max, maxp;
	max = 0;
	for (i = 0; i < m; ++i)
	{
		//对于有j个点共线的直线,有j * (j - 1) / 2 个组合
		for (j = 1; (j * (j - 1) / 2) != line[i].count; ++j);

		//找出共线点数最多的那条直线
		if (max < j)
		{
			max = j;
			maxp = i;
		}
	}

	//输出所有直线中,通过点数最多的那条直线的点数,和通过的各点坐标
	printf("max points = %d\n", max);

	for (i = 0; i < n-1; ++i)
	{
		//如果点在这条直线上
		if (fabs(point[i].y - line[maxp].k*point[i].x - line[maxp].y_axis) < 1e-5)
		{
			printf("(%.2f,%.2f)\n", point[i].x, point[i].y);
		}
	}


	return 0;
}

/*
回顾一下这道题,问题核心在于求出m个点能构成多少条直线,
同一条直线上j个点能组成多少条线段
*/

【测试数据】

与方法1相同

【测试结果】

与方法1相同,通过的还是那几个点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值