【备战蓝桥杯】11.直线(2)——直线表达优化

178 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。 给定平面上 2×32 × 3 个整点 {(x,y)0x<2,0y<3,xZ,yZ}\{(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z\},即横坐标是 0011 (包含 0011) 之间的整数、纵坐标是 0022 (包含 0022) 之间的整数的点。这些点一共确定了 1111 条不同的直线。 给定平面上 20×2120 × 21 个整点 {(x,y)0x<20,0y<21,xZ,yZ}\{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z\},即横坐标是 001919 (包含 001919) 之间的整数、纵坐标是 002020 (包含 002020) 之间的整数的点。请问这些点一共确定了多少条不同的直线。

思路详解——第二次迭代版本

上次我们使用的直线方程是斜截式,由于斜率k是同时含分子分母的数,表达比较繁琐。

本次经过迭代后的代码使用的直线方程是一般式:Ax+by+C=0Ax+by+C=0。 经过计算,设两点(x1,y1),(x2,y2)(x_1,y_1),(x_2,y_2)确定一条直线,则:

A=y2y1A=y_2-y_1
B=x2x1B=x_2-x_1
C=(x2x1)y1(y2y1)x1;C=(x_2-x_1)*y_1-(y_2-y_1)*x_1;

注意,两条完全相同的直线其A,B,CA,B,C不一定是相同的,而是有可能具有相同的倍数,如同2x3y+4=02x-3y+4=04x+6y8=0-4x+6y-8=0是同一条直线。因此对于每两个点,它们算出来的A,B,C,A,B,C,我们需要化成最简,方法就是同时除以它们三个共同的gcdgcd.三个数的gcdgcd计算方式如下: gcd(a,b,c)=gcd(gcd(a,b),c)gcd(a,b,c)=gcd(gcd(a,b),c) (其实对于任意多的数都可以如此递归求gcdgcd) 同时,如果AA是负数,把a,b,ca,b,c同时乘以1-1。这样,所有计算出来的直线方程都转换为了最简式且AA非负,判断相等的方式变味了最简单的:A,B,CA,B,C同时相等。

struct line{
	int a,b,c;//坚持a是正数 
};
line getline(int x1,int y1,int x2,int y2){
	int a=y2-y1;
	int b=x2-x1;
	int c=(x2-x1)*y1-(y2-y1)*x1;
	int tmp=gcd(abs(a),abs(b),abs(c));
	a=a/tmp,b=b/tmp,c=c/tmp;
	if(a<0)a=-a,b=-b,c=-c;
	return line{a,b,c};
}

最后还有一个小点需要注意那就是这个表达方式不支持k=0和k不存在的情况。解决方法就是y_1=y_2或x_1=x_2时跳过,最后在结果上加41即可。

本次的代码,相比上次的繁琐的表达方式有了很大改进。下面搬出完整代码。

完整代码

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define pp pop_back
using ll=long long;
using db =double;
int gcd(int a,int b){
	if(a<b)return gcd(b,a);
	if(b==0)return a;
	return gcd(b,a%b);
}
int gcd(int a,int b,int c){
	return gcd(gcd(a,b),c);
}
struct line{
	int a,b,c;//坚持a是正数 
	bool operator==(const line &x)const{
		return a==x.a&&b==x.b&&c==x.c;
	}
};
line getline(int x1,int y1,int x2,int y2){
	int a=y2-y1;
	int b=x2-x1;
	int c=(x2-x1)*y1-(y2-y1)*x1;
	int tmp=gcd(abs(a),abs(b),abs(c));
	a=a/tmp,b=b/tmp,c=c/tmp;
	if(a<0)a=-a,b=-b,c=-c;
	return line{a,b,c};
}
vector<line> lines;
int main(){
	cout<<lines.size()<<' ';
	for(int x1=0;x1<=19;x1++){
		for(int y1=0;y1<=20;y1++){
			for(int x2=0;x2<=19;x2++){
				for(int y2=0;y2<=20;y2++){
					if(y2-y1==0||x2-x1==0)continue;
					auto li=getline(x1,y1,x2,y2);
					int flag=1;
               for(auto li2:lines){
                  if(li2==li)flag=0;
               }
               if(flag)lines.pb(li);
				}
			}
		}
	}
	cout<<lines.size()+20+21;
	return 0;
}