1. 程式人生 > >類歐幾里德

類歐幾里德

類歐幾里德
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為整數。

在這裡插入圖片描述