(File IO): input:math.in output:math.out
Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits
Description
Input
输入有多组测例,每组测例有一行,为4 个整数x1,y1, x2, y2,含义见题目描述。输入文件以EOF 结束。
Output
Sample Input
3 0 1 2
6 0 4 0
Sample Output
5
0
Data Constraint
Source / Author: 黄施霖 math
题目大意 :
给你两个向量 a,b , 求正整数对x,y,使(ax + by)^1最小 , 输出这个最小值 。 注意x,y不能同时为0。
题解:
二维欧几里得算法 。 (自行bd)
首先 , 对于共线的两个向量 , 答案是 0 。因为你可以让他们先反向 , 然后长度变成他们的lcm ,相加抵消。
剩余的情况还要分情况讨论。
为了方便讨论 , 我们始终让 |a| <=|b|且a 点乘b 大于 0 ,也就是两点在y轴的同侧。
结论1:当
, 也就是θ >=60度 , 答案是 |a| ^ 2。
注:两向量夹角θ 的 cos , 即cos θ 公式 = a·b / (|a| * |b|).
至于为什么θ大于60度cos就小于了0.5 , 自己画图,看看一个大于60的角 ,和一个小于60的角的cos就知道了。
其他证明参考论文。
好吧 , 我TM还是有点良心的。
证明:
我们令a为a的模长 , b同理 (本人懒)
设A = 答案模长的平方 = |ax + by|^2 = (ax)^2 + (by)^2 + 2cosθaxby
设C = (ax)^2 + (by)^2 - |ax||by| = (|ax| - |by|)^2 +|ax||by|
我们假设 A >=C
当x=0 , y不为0 , 则|ax + by|^2 >= (by)^2 >= (ax)^2 >= a^2
当y=0 , x不为0 , 则|ax + by|^2 >= (ax)^2 >= a^2 。
当xy都不为0 , |ax||by| >= |a||b| >= a^2
综上 , 当 A >= C , 答案a^2一定是可以取到的最小值 。
接下来证明在cos < 0.5 的情况下 , A >=C
当xy > =0 , A >= C显然成立。
当xy<0 , A = (ax)^2 + (by)^2 + 2cosθaxby = (ax)^2 + (by)^2 - 2cosθ|ax||by|
C = (ax)^2 + (by)^2 - |ax||by| .
因为cos <= 0.5 ,2cos <=1
所以 2cosθ|ax||by| <=|ax||by| , 所以 (ax)^2 + (by)^2 - 2cosθ|ax||by| >= (ax)^2 + (by)^2 - |ax||by| . 即 A>=C,得证 。
综上 : 无论xy取值如何 , 当cos <= 0.5 , 答案是 |a| ^ 2。
结论2 (a,b)这两个向量的答案等于(a , b + ak)这个向量对的答案。
证明 :
令|xa+yb|为所求答案,则|x(a−kb)+(y+kx)b|也是(自己化一下式子),即(a, b)对应着答案(a - kb) , b) , 形式和假设相同 ,得证。
这样我们就可以通过不断变换a,b , 直到他们的夹角要大于60度。
怎么变换呢 ?
根据二维欧几里得 , 我们只需求出b在a的投影 : 具体参考“向量的投影”。
如图 : (注意ab的位置和上一幅图不一样) b在a的投影是c。
求投影公式 c(标量) = a * b / |a|;
我们可以计算c是a的k倍 。 有一种简单简洁的计算方式 k = (a*b)/ (a*a) , 后面的a*a其实就是a的模长的平方 ,
即 (a*b)/ (a*a) =[ a * b / |a|] /|a|,这就是k了。
如果k连1到不到 , 如上图 ,那么 :我们把b变成 b - a
易得角OAB比θ大(做对称)。
若k比1要大了 , 我们就把k下取整 , 这样a*k刚好没超过c 。 然后 b = b - a*k , 也可以起到增大角度的作用。
注意时刻保持我们的约定 :
|a| <=|b|且a 点乘 b 大于 0 ,也就是两点在y轴的同侧。
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mcy(a,b) memcpy(a,b,sizeof(a))
#define ll long long
#define inf 2000000000
#define N 110
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
int sqr(int x){return x*x;}
struct vec
{
int x,y;
double length(){return sqrt((double)sqr(x) + sqr(y));}
int operator * (vec b) {return x * b.x + y*b.y;}
vec operator * (int b) {return (vec){x * b , y * b} ;}
vec operator + (vec b) {return (vec){x + b.x , y + b.y};}
vec operator - (vec b) {return (vec){x - b.x , y - b.y};}
int operator | (vec b) {return x * b.y + y * b.x;}
}a , b , c;
double get_cos(vec a,vec b)
{
return (double)(a*b) / (a . length() * b.length()) ;
}
int main()
{
open("math");
while(~scanf("%d%d%d%d",&a. x , &a.y , &b.x ,&b.y) )
{
if((a|b) == 0 ) {printf("0");continue;}
if(a*a > b*b) swap(a,b);
if(a*b < 0 ) b = b * -1;
double c_a = get_cos(a,b);
while(c_a > 0.5)
{
if(a*a > b*b) swap(a,b);
if(a*b < 0 ) b = b * -1;
if(a*b / (a*a) <= 1) b = b - a;
else
{
c = a * ((a*b) / (a*a)) ;
b = b - c;
}
c_a = get_cos(a,b);
}
printf("%d\n" , min(a*a , b*b) );
}
return 0;
}