题目描述
二维平面上有 n 只蚂蚁,每只蚂蚁有一条线段作为活动范围,第 i 只蚂蚁的活动范围的两个端点为 (uix, uiy),(vix, viy)。现在蚂蚁们考虑在这些线段的交点处设置会议中心。为了尽可能节省经费,它们决定只在所有交点为整点的地方设置会议中心,请问需要设置多少个会议中心?
输入格式
输入共 n + 1 行。第一行为一个正整数 n。后面 n 行,每行 4 个由空格分开的整数表示 uix, uiy, vix, viy。
输出格式
输出共 1 行,一个整数表示答案。
样例输入
4
0 0 4 4
0 4 4 0
2 0 0 4
2 1 2 3
样例输出
2
提示
【样例说明】
所有线段之间共有 3 个不同的交点:(0, 4),(43,43),(2, 2), 其中整点有 2 个:(0, 4),(2, 2)。
【评测用例规模与约定】
对于 20% 的评测用例,保证 0 ≤ uix, uiy, vix, viy ≤ 100。对于 100% 的评测用例,保证 n ≤ 500,0 ≤ uix, uiy, vix, viy ≤ 10000,保证任意蚂蚁的活动范围不会退化成一个点,不保证任意两条线段之间交点数量有限。
代码简介
使用C++的 set 来存储符合条件的坐标,以防止坐标重复。
在计算时采用 克拉默法则 计算线性方程组的解。
注意
1.防止坐标重复
2.注意是线段而不是直线,交点坐标必须是在线段范围内
3.考虑两条线段重合的情况
代码示例
#include<iostream>
#include<set>
#include<optional>
using namespace std;
struct zuobiao{
int x;
int y;
// 定义 operator< 用于比较
bool operator<(const zuobiao& other) const {
if (x != other.x) {
return x < other.x; // 优先比较 x
}
return y < other.y; // x 相同则比较 y
}
};
// 根据克拉默法则计算线性方程组的解
bool juzhen(int x1[], int x2[], int B[], zuobiao& result){
// 初始化矩阵A
int A[2][2]={0};
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
if(i==0){
A[j][i]=x1[j];
}
else
A[j][i]=x2[j];
}
}
// 计算A的行列式
int a=A[0][0]*A[1][1]-A[0][1]*A[1][0];
// cout<<"a= "<< a <<endl;
// 行列式的值等于0,无交点
if(a==0){
// cout<<"无交点"<<endl;
return false;
}
// 下面都是克拉默计算公式
int tx1=B[0]*A[1][1]-B[1]*A[0][1];
// cout <<"tx1= "<<tx1<<endl;
int tx2=A[0][0]*B[1]-A[1][0]*B[0];
// cout <<"tx2= "<<tx2<<endl;
// 判断交点是否为整数
if(tx1%a!=0||tx2%a!=0){
// cout<<"交点坐标为小数"<<endl;
return false;
}
int xx1=tx1/a;
int xx2=tx2/a;
// cout<< "交点坐标"<<xx1<<' '<<xx2<<endl;
result = zuobiao{xx1, xx2};
return true;
}
struct Result {
int x1;
int x2;
int B;
};
// 根据坐标计算方程的一般形式Ax+By=C
Result fangcheng(int x1,int y1,int x2,int y2){
int tX1=y1-y2; // A
int tX2=(x1-x2)*(-1); // B
int tC=tX1*x1+tX2*y1; // C
return {tX1,tX2,tC};
}
int main(){
zuobiao result;
set <zuobiao> xy;
// 输入初始数据
int n=0;
cin>>n;
int line[n][4]={0};
for(int i=0;i<n;i++){
for(int j=0;j<4;j++){
scanf("%d",&line[i][j]);
}
}
// 穷举所有线段组合
for(int i=0;i<n-1;i++){
for(int j=i+1;j<n;j++){
// 得到两个方程的未知数的系数和常数项
auto f1 = fangcheng(line[i][0],line[i][1],line[i][2],line[i][3]);
auto f2 = fangcheng(line[j][0],line[j][1],line[j][2],line[j][3]);
int X1[]={f1.x1,f2.x1};
int X2[]={f1.x2,f2.x2};
int B[]={f1.B,f2.B};
// 判断两线段是否重合
if((f1.x1*f2.x2==f2.x1*f1.x2)&&(f1.x2 * f2.B == f2.x2 * f1.B)){
// 如果两线段重合,找出重合部分的X坐标的范围
int min_x=max(min(line[i][0],line[i][2]),min(line[j][0],line[j][2]));
int max_x=min(max(line[i][0],line[i][2]),max(line[j][0],line[j][2]));
for(int k=min_x;k<=max_x;k++){
// 在这个范围里,如果坐标为整数,这放入xy里
if((f1.B-f1.x1*k)%f1.x2==0){
int y=(f1.B-f1.x1*k)/f1.x2;
xy.insert(zuobiao{k,y});
}
}
}
// 判断两线段是否有交点
else if(juzhen(X1,X2,B,result)){
// 判断交点是否在线段范围内
if((min(line[i][0],line[i][2])<=result.x && result.x<=max(line[i][0],line[i][2]))&&
(min(line[j][0],line[j][2])<=result.x && result.x<=max(line[j][0],line[j][2]))&&
(min(line[i][1],line[i][3])<=result.y && result.y<=max(line[i][1],line[i][3]))&&
(min(line[j][1],line[j][3])<=result.y && result.y<=max(line[j][1],line[j][3]))){
// 满足上述条件,加入xy
xy.insert(result);
// printf("%d %d\n", result.x,result.y);
}
// result.insert(xy);
}
}
}
// for(auto it=xy.begin();it!=xy.end();it++){
// printf("%d %d\n", it->x,it->y);
// }
// 输出xy里符合的坐标个数
cout<<xy.size()<<endl;
return 0;
}