Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。
给定平面上 2 × 3 个整点 {(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z},即横坐标是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数的点。这些点一共确定了 11 条不同的直线。
给定平面上 20 × 21 个整点 {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},即横坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之间的整数的点。请问这些点一共确定了多少条不同的直线。
思路
做算法题,很容易走一些不该走的弯路,比如明明是无脑遍历,却总想着要动态规划。怎么大致确定每一道题的大致思路方向呢?一个大方向就是——看它的数据量。如这道题所示,两点确定一条直线,一共个点,也就是说大概在的复杂度,遍历的话完全可以接受。因此果断将思路集中在暴力遍历所有点。
但是这道题重点的重点还是判断两个直线是不是同一条。一开始我痴迷于画图找规律,总想着有什么O(1)解法,但是事实证明有些事还是要交给聪明的计算机来做~~~
对于任意两个点形成的直线,我们只需要求出他的方程式的k和b,然后就可以通过 和 是否相等来判断是不是一条直线了。当然要考虑 不存在的情况,只需简单的在结果上加上20即可。
对于两个点(其中),设其直线方程为 ,则经过计算得:
由于k是两数相除,直接求解判相等会出现精度问题。因此,需要将其化为最简分数然后保存分子分母。方法就是令分子和分母同时除以两者的gcd。 以下是求最简k的函数。
void computek(int &dx,int &dy){
int tmp=gcd(dx,dy);
dx=dx/tmp;
dy=dy/tmp;
}
判断k是否相等,只需要判断分子和分母是否同时相等即可。需要注意的是符号问题需要额外处理一下。
若 相等,则判断 是否相等。设两直线 设 得 ,又回到了判断k相等的问题。 判断函数如下:
struct line{
int kx,ky;
bool fu;
int x1,y1;
};
bool iseql(line &a,line &b){
if(a.kx==b.kx&&a.ky==b.ky&&a.fu==b.fu){
if(a.x1==b.x1&&a.y1==b.y1){
return 1;
}else{
int dy=a.y1-b.y1;
int dx=a.x1-b.x1;
int fu=dx*dy<0;
dy=abs(dy);
dx=abs(dx);
computek(dx,dy);
if(dx==a.kx&&dy==a.ky&&fu==a.fu){
return 1;
}
}
}
return 0;
}
至此所有主要问题都已解决。
完整代码
#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);
}
void computek(int &dx,int &dy){
int tmp=gcd(dx,dy);
dx=dx/tmp;
dy=dy/tmp;
}
struct line{
int kx,ky;
bool fu;
int x1,y1;
};
bool iseql(line &a,line &b){
if(a.kx==b.kx&&a.ky==b.ky&&a.fu==b.fu){
if(a.x1==b.x1&&a.y1==b.y1){
return 1;
}else{
int dy=a.y1-b.y1;
int dx=a.x1-b.x1;
int fu=dx*dy<0;
dy=abs(dy);
dx=abs(dx);
computek(dx,dy);
if(dx==a.kx&&dy==a.ky&&fu==a.fu){
return 1;
}
}
}
return 0;
}
vector<line> lines;
int main(){
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++){
int dy=y2-y1;
int dx=x2-x1;
if(dx==0)continue;
int fu=dx*dy<0;
dy=abs(dy);
dx=abs(dx);
computek(dx,dy);
line a={dx,dy,fu,x1,y1};
int flag=1;
for(auto b:lines){
if(iseql(a,b))flag=0;
}
if(flag)lines.pb(a);
}
}
}
}
cout<<lines.size()+20;
return 0;
}
总结
这次算是本道题的初代版本,由于涉及到正负号问题所以写的有点乱。下次会推出迭代版本,跑的更快(这篇文章的代码我跑了将近半分钟才跑完)