類歐幾里德
阿新 • • 發佈:2018-12-15
類歐幾里德
bzoj 2187: fraction 類歐幾里德演算法
題意:給你4個正整數a,b,c,d,求一個最簡分數 p / q滿足 a / b < p / q < c / d,若有多組解,輸出q最小的一組,若仍有多組解,輸出p最小的一組。
#include<bits/stdc++.h> using namespace std; typedef long long LL; LL gcd(LL a,LL b){//求AB最大公因數 if (!b) return a; else return gcd(b,a%b); } void sim(LL &a,LL &b){LL d=gcd(a,b);a/=d;b/=d;}//A,B共除GCD變互質,實即約分 void solve(LL a,LL b,LL c,LL d,LL &p,LL &q){ sim(a,b);sim(c,d);//AB,CD約分 LL x=a/b+1,y=c/d+(c%d>0)-1;//X是大於A/B的最小整數,Y是小於C/D的最大整數 if (x<=y) p=x,q=1;//X已經小於Y,注意結束語句下PQ是同時取最小值的 else if (!a) p=1,q=d/c+1;//A是0則只考慮右邊即可,注意結束語句下PQ同時取最小值 else if (a<=b&&c<=d) solve(d,c,b,a,p,q),swap(p,q);//左右都是真分數,就變倒數,注意變號 else solve(a%b,b,c-d*(a/b),d,p,q),p+=q*(a/b);//假分數就式子各部分全減(a/b),如此反覆 } int main(){ LL a,b,c,d;//輸入ABCD,求a / b < p / q < c / d while (scanf("%lld%lld%lld%lld",&a,&b,&c,&d)!=EOF){ LL p,q; solve(a,b,c,d,p,q); printf("%lld/%lld\n",p,q); } }
JZOJ 3736. 【NOI2014模擬7.11】數學題(math)
#include<bits/stdc++.h> using namespace std; #define LL long long LL const Mxa=1e4,Inf=1e9;double Eps=1e-6; LL X1,X2,Y1,Y2; LL Dot(LL X1,LL Y1,LL X2,LL Y2){return X1*X2+Y1*Y2;}//點乘 double Abs(LL X1,LL Y1){return sqrt(X1*X1+Y1*Y1);}//求模 int main(){ double Cos,M1,M2;//餘弦值,兩個模 while(scanf("%lld%lld%lld%lld",&X1,&Y1,&X2,&Y2)!=EOF){ if(Dot(X1,Y1,X2,Y2)<0)X1=-X1,Y1=-Y1;//如果點乘小於零則令X1取反實即成銳角 double xx=Dot(X1,Y1,X2,Y2),x=Abs(X1,Y1),y=Abs(X2,Y2);//求出兩向量點乘與模長 if(X1*Y2==X2*Y1){printf("0\n");continue;}//叉乘相等即共線直接0 while((Cos=Dot(X1,Y1,X2,Y2)/Abs(X1,Y1)/Abs(X2,Y2))>0.5){//COS值大於1/2即小於60度,就取反方向 M1=Abs(X1,Y1),M2=Abs(X2,Y2);//讀出兩模 if(M1>M2+Eps)swap(X1,X2),swap(Y1,Y2),swap(M1,M2);//M1模長就較換兩向量的X,Y,模三個引數 LL Tmp=M2/M1;//得到模長比值 if(M2*Cos-M1*Tmp<M1*(Tmp+1)-M2*Cos)//圖中CE段小於ED段 X2-=X1,Y2-=Y1;//長邊OB加一單位OA,則夾角就變大了 else X2=-X1*(Tmp+1)+X2,Y2=-Y1*(Tmp+1)+Y2,X1=-X1,Y1=-Y1; //如果CE大於ED段,OB減去OD段,然後OA也取反(即使是大於60度也要是在0~90度範圍內! } printf("%lld\n",min(X1*X1+Y1*Y1,X2*X2+Y2*Y2));//最後輸出向量和模平方 } return 0; }
Sample Input
3 0 1 2
6 0 4 0
Sample Output
5
0
結論1.兩個向量夾角大於60度,肯定答案會取兩個向量模長的較小值。
結論2. (a, b)所對應的答案,和(a, b + ka)一致,其中k為整數。