第十二届蓝桥杯省赛 C/C++大学B组 试题C:直线

试题题目原题:

请添加图片描述
本题答案:40257
解题思路
  一、在平面直角坐标系统XOY中,有点集{(x,y)|0≤x<2,0≤y<3,x ϵ Z,y ϵ Z},确定了2×3=6个点。每两个点都能确定一条直线,那么该六个点就能确定 ( 6 2 )   \tbinom{6}{2}\, (26)=15条直线,其中包含3条平行于x轴(包含x轴)的直线,2条平行于y轴(包含y轴)的直线。为了保证直线不重复,从第0个点(原点(0,0))出发,走过的点不再走一遍,每个点都与剩下的点连接。他们分别是

点1点2直线方程直线方程(一般形式)ABCvalue
(0,0)(0,1)x = 01x + 0y +0 =0100y轴
(0,0)(0,2)x = 01x + 0y +0 = 0100y轴
(0,0)(1,0)y = 00x + 1y + 0 = 0010x轴
(0,0)(1,1)y = x(-1)x+1y + 0 =0-1101
(0,0)(1,2)y = -2x(-2)x +1y+ 0=0-2102
(0,1)(0,2)x = 01x + 0y + 0 = 0100y轴
(0,1)(1,0)y =- x+11x + 1y+(-1)= 011-13
(0,1)(1,1)y = 10x + 1y +(-1)=001-1平行于x轴
(0,1)(1,2)y = x+1(-1)x+1y+(-1)=0-11-14
(0,2)(1,0)y = -2x+22x + 1y +(-2) = 021-25
(0,2)(1,1)y = -x+21x + 1y +(-2) = 011-26
(0,2)(1,2)y =20x + 1y +(-2)=001-2平行于x轴
(1,0)(1,1)x = 11x +0y +(-1)= 010-1平行于y轴
(1,0)(1,2)x = 11x +0y +(-1)= 010-1平行于y轴
(1,1)(1,2)x = 11x +0y+(-1)= 010-1平行于y轴

所以点集{(x,y)|0≤x<2,0≤y<3,x ϵ Z,y ϵ Z}所连接起来的直线有 6+2+3 = 11 条;

  二、同理,对于再平面直角坐标系统XOY中,点集{(x,y)|0≤x<20,0≤y<21,x ϵ Z,y ϵ Z},可以确定20×21=420个点。420个点能确定 ( 420 2 ) = 87990   \tbinom{420}{2}=87990\, (2420)=87990条直线。
  使用C语言编写程序,用结构体STOP存储点的坐标,用结构体LINE存储直线的四个元素,A,B,C,value,其中value的值用来去重。STOP定义的结构体数组长度是col×row,结构体LINE定义的结构体数据长度可以假设为100000。
1、计算直线方程的方法:
   根据数学平面几何知识,两点确定一条直线,两点式直线方程表达式为:
  ( y − y 1 ) / ( y 1 − y 2 ) = ( x − x 1 ) / ( x 1 − x 2 )   \ (y-y_{1})/(y_{1}-y_{2}) = (x-x_{1})/(x_{1}-x_{2}) \,  (yy1)/(y1y2)=(xx1)/(x1x2)
   变形后
  ( y − y 1 ) ( x 1 − x 2 ) = ( x − x 1 ) ( y 1 − y 2 )   \ (y-y_1)(x_1-x_2) = (x-x_1)(y_1-y_2) \,  (yy1)(x1x2)=(xx1)(y1y2)
   再去括弧,移项,得一般形式方程:
  ( y 1 − y 2 ) x + ( − ( x 1 − x 2 ) ) y + ( x 1 ∗ y 2 − y 1 ∗ x 2 ) = 0   \ (y_1-y_2 )x+(-(x_1-x_2 ))y+(x_1*y_2-y_1*x_2) =0 \,  (y1y2)x+((x1x2))y+(x1y2y1x2)=0
   对应于一般式 Ax+By+C = 0 分别是:
  A = y 1 − y 2 , B = − ( x 1 − x 2 ) = x 2 − x 1 , C = x 1 ∗ y 2 − y 1 ∗ x 2   \ A=y_1-y_2,B=-(x_1-x_2 )=x_2-x_1,C = x_1*y_2-y_1*x_2 \,  A=y1y2B=(x1x2)=x2x1C=x1y2y1x2
   其中平行于x轴(包含x轴)直线和平行于y轴(包含y轴)直线,总和就是x轴和y轴上的点是的和,在计算直线方程时,不需要计算这种直线。
   特别注意:需要对A,B,C约分,把 Ax+By+C = 0 化为最简形式,方便去重。

2、去重的方法:
  遍历直线LINE结构体数组,如果两个结构体中A,B,C数据相同,那么就认为这两个直线是重合的直线。第一条直线的不能判断它与哪条直线重合,那么把该直线的value数据赋值为1。从第二条直线开始,与它前面的所有直线作比较,如果前面有与该直线重合的直线,那么该直线的value数据不变,等于前一个直线的value数值;如果前面没有与该直线重合的直线,那么该直线的value数据将在前一个直线的value数值加1。遍历所有的直线后,最后一条直线的value值就是去重后的直线条数。
  注意:最终的直线条数是 最后一条直线的value值 加上 平行于x轴(包含x轴)直线和平行于y轴(包含y轴)直线和。

#include<stdio.h>
#define max 1000000
#define row 21 
#define col 20 
struct SPOT  //所有点的坐标结构 
{
	int x;
	int y;
}spot[col*row]; 
struct LINE //直线结构体 存贮A,B,C 和value 
{
	int A;
	int B;
	int C;
	int value; 
}line[max];
int sum = 0;  //累计直线的数量 
int sczx(int,int,int,int); //生成两点确定的直线方程 
int gcd(int,int);
int main()
{
	int k,i,j;
	k=0;
	for(i=0;i<col;i++)  //初始化各个点坐标 
	{
		for(j=0;j<row;j++)
		{
			spot[k].x = i;
			spot[k].y = j;
			k++;
		}
	}
	for(i=0;i<col*row;i++)
	{
		for(j=i+1;j<col*row;j++)
		{	
			sczx(spot[i].x,spot[i].y,spot[j].x,spot[j].y);
		}
	}
	for(i=1; i<sum; i++)         //去重方法实现 
	{
		for(j=0; j<i; j++)
		{
			if( line[i].A == line[j].A && line[i].B == line[j].B && line[i].C == line[j].C )
			{
				line[i].value = line[i-1].value;
				break;
			} 
			line[i].value = line[i-1].value + 1;
		}
	} 
	/*for(i=0; i<sum; i++)   //检测代码1  
	//{
	//	printf("第%d条直线的参数:A=%d ,B=%d , C=%d , value=%d \n",i,line[i].A,line[i].B,line[i].C,line[i].value);
	//}
	*/
	printf("%d",line[sum-1].value + col + row);
	return 0;
} 
int sczx(int x1,int y1,int x2,int y2){  //计算出(x1,y1)(x2,y2)两点确定的直线方程。 
	int A,B,C; 
	if(x1 == x2){ return 0; }
	if(y1 == y2){ return 0; }
	if(x1 != x2 && y1 !=y2){ A = y1-y2; B = x2-x1; C = x1*y2 - y1*x2;	}
	int fm; 
	if(C == 0){ fm = gcd(A,B);}  //过原点,且不平行于x或y轴的直线 也需要对A和B约分处理。 
	else{fm = gcd(gcd(A,B),C);}
	line[sum].A = A/fm;
	line[sum].B = B/fm;
	line[sum].C = C/fm;
	line[sum].value = 1;
	//printf("%dx + %dy + %d = 0  过点(%d,%d)(%d,%d) \n",line[sum].A,line[sum].B,line[sum].C,x1,y1,x2,y2); //检测代码2 
	sum++;
	return 0;
}
int gcd(int m,int n){    //寻找出m,两个数值绝对值的最大公约数。 
	if(m == 0 || n == 0) return 1;
	if(m<0){ m = 0 - m; }
	if(n<0){ n = 0 - n; }
	if(m < n){
		m = n - m;
		n = n - m;
		m = n + m; 
	}
	int r = m % n;
	while(r != 0)
	{
		m = n;
		n = r;
		r = m%n;
	}
	return n;
}
  • 14
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值