本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
题目链接
题目
Little Desprado2 is a student of Springfield Flowers Kindergarten. On this day, he had just learned how to draw triangles on grid coordinate paper. However, he soon found it very dull, so he came up with a more interesting question:
He had drawn two integral points of the triangle on the grid paper, and he denotes them (x1,y1) and (x2,y2). Now, he wanted to know the answer to the following question: where can he draw the third point (x3,y3) so that the area of the triangle is positive but minimized?
Obviously, he can't solve this problem. Can you tell him the answer?
Please note that your answer's coordinates must consist of integers because he is drawing on grid paper, and the triangle shouldn't be a degenerated triangle to keep the area positive.
题目大意
对于二维平面内给定的两个坐标是整数的点 和 ,找到另一个坐标是整数的点使得三个点构成的三角形面积为正且面积最小。
多组数据。
思路
看题解直接用了向量,赛中没有往这个方向考虑,这里给出我的奇奇怪怪的思路。
我们称呼横纵坐标均为整数的点为整点。
首先,点 和 可以确定一条直线
以这位于这条线的线段为底边,假设第三个点的坐标为 ,如果我们确定了 ,可以求出来对应在直线上的 值 。又因为底边的斜率已经确定了,我们想要找距离直线最近的点,其实就是在找与 差最小且为正的整数作为 。
显然枚举 并不现实,而我们实际上想要找到与直线上下相邻的所有整点中到直线曼哈顿距离最小的点,一种合理的构造方法是找到直线中 坐标是整数, 坐标的小数部分最小且为正的整点,让其舍去 坐标的小数部分。
观察上式,显然 是整数, 可以取任意整数,则 也可以取任意整数记为 ,令 ,,若 则 均取反,又
则我们的目标转化为最小化 。而 相当于 ,显然最小答案是 ,即 和 的最大公约数,可以用扩展欧几里得算法求解 值。则答案是 。
在上述推导中我们默认了 ,可以用通解公式对 进行转化使其满足条件。 或者对求得的 进行观察,容易发现如果 ,应该取对应 坐标直线下方的点。C 中负数对正数取余会向上保留结果(不同编译器好像有差异),所以计算出的 坐标应该再减 1。
代码
#include <stdio.h>
#include <iostream>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
LL gcd(LL a,LL b)
{
if (b==0) return a;
return gcd(b,a%b);
}
LL exgd(LL a,LL b,LL &x,LL &y)
{
if (!b)
{
x=1;
y=0;
if (a<0) a=-a,x=-1;
return a;
}
LL gd=exgd(b,a%b,x,y),t=x;
x=y;
y=t-a/b*x;
return gd;
}
LL x1,y1,x2,y2;
int solve()
{
cin>>x1>>y1>>x2>>y2;
if (x1==x2) cout<<x1+1<<" "<<y1<<"\n";
else if (y1==y2) cout<<x1<<" "<<y1+1<<"\n";
else if (abs(x1-x2)==gcd(abs(x1-x2),abs(y1-y2))) cout<<x1<<" "<<y1+1<<"\n";
else
{
LL a,b,u,v;
a=x1-x2;
b=y1-y2;
if (a<0) a*=-1,b*=-1;
//根据通解公式调整 v 的写法
LL gd=exgd(a,b,u,v);
LL p=a/gd;
if (b*v!=gd)
{
if (b>0) v+=(1-v/p)*p;
else v+=(-1-v/p)*p;
}
cout<<v+x1<<" "<<b*v/a+y1<<"\n";
/*
特判负数的写法
exgd(a,b,u,v);
LL ansx=v+x1;
LL ansy=b*v/a+y1;
if (b*v<0) ansy--;
cout<<ansx<<" "<<ansy<<"\n";
*/
}
}
int main()
{
int T=1;
for (scanf("%d",&T);T--;) solve();
return 0;
}