方法一(子问题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相同,通过的还是那几个点