一、歐幾里得算法及其證明
1.定義:
歐幾里得算法又稱輾轉相除法,用於求兩數的最大公約數,計算公式為GCD(a,b)=GCD(b,a%b);
2.證明:
設x為兩整數a,b(a>=b)的最大公約數,那么x|a,x|b;
①由整數除法具有傳遞性(若x能整除a,x能整除b,那么x可整除a,b的任意線性組合)知x|a-b;
②設x不是b的因子,則x不是b和a-b的公因子;設x不是a的因子,則x不是b和a-b的公因子;所以可以得出GCD(a,b)=GCD(b,a-b);
③由a>=b知,a可表示為a=b*q+r;則a減去q個b剩下的數字即為r,所以GCD(a,b)=GCD(b,a%b);
3.一般代碼:
(1)遞歸形式:
int gcd(int a,int b){return b?gcd(b,a%b):a;}
(2)迭代形式:
int gcd(int a,int b){
for(;;) {
if(b==0)return a;
int temp=a%b;
a=b;
b=temp;
}
}
4.幾個性質:
(1)若GCD(a,b)=1,那么a,b兩數互質。
(2)GCD(a,2a)=a;
(3)GCD(a,0)=a;
(4)GCD(a,b)=GCD(-a,b)=GCD(a,-b)=GCD(-a,-b);
(5)LCM(a,b)GCD(a,b)=ab(LCM為兩數小公倍數);
(6)GCD(n,n+1)=1;
證明:
假設他們不是互素的,有公共因子q
n = p1 * 1,n + 1 = p2 * q;n+1 - n = q(p2 - p1)
則q(p2-p1) = 1;其中p2,p1均為整數,q >=2,可證不等。得證。
二、相關題目
1.[洛谷P1372]又是畢業季I
Description
老師想要挑出默契程度最大的k個人參與畢業晚會彩排。可是如何挑呢?老師列出全班同學的號數1,2,……,n,並且相信k個人的默契程度便是他們的最大公約數(這不是迷信哦~)。這可難為了他,請你幫幫忙吧!PS:一個數的最大公約數即本身。
輸入格式:兩個空格分開的正整數n和k。(n>=k>=1)
輸出格式:一個整數,為最大的默契值。
Solution
1.注意:“一個數的最大公約數即本身”:我們可以從性質(2)考慮:當兩個數是倍數時,最大公約數即為較小的數,那么此時相對同一范圍的其他組合這兩個數的最大公約數相對較大。
2.在討論幾種特殊情況:k=1時,ans=n;k=2時,若n為偶數,則ans=n/2,若n為奇數,則ans=(n-1)/2;
3.有上述討論我們發現:滿足k*a<n的a的最大值即為答案。即選中的數字分別為a,2a,3a,......,ka,所以答案為a/b;
Code
#include<iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
cout<<a/b<<"\n";
return 0;
}
2.[洛谷P1170]兔八哥與獵人
Description
兔八哥躲藏在樹林旁邊的果園里。果園有M × N棵樹,組成一個M行N列的矩陣,水平或垂直相鄰的兩棵樹的距離為1。兔八哥在一棵果樹下。獵人背着獵槍走進了果園,他爬上一棵果樹,准備殺死兔八哥。如果獵人與兔八哥之間沒有其它的果樹,獵人就可以看到兔八哥。現己知獵人和兔八哥的位置,編寫程序判斷兔子所在的位置是否安全.
輸入格式:第一行為n,表示有n(n ≤ 100,000)組數據,每組數據的第一行為兩個正整數ax和ay,表示獵人的位置,第二行為兩個正整數bx和by,表示兔八哥的位置(1 ≤ ax, ay, bx, by ≤ 100,000,000)。
輸出格式:共有n行,每行為“yes”或“no”表示兔八哥的位置是否安全。
Solution
1.讀題后我們可以將題目化簡:求兩坐標為整數的點確定的直線上兩點間是否還存在另一坐標均為整數的點;
2.那么我們可以以獵人的坐標為原點,建立坐標系,設獵人(x1,y1),兔子(x2,y2),那么我們把兔子的坐標改為(x2-x1,y2-y1);
3.那么如何確定該點與原點間沒有其他坐標為整數的點呢?通過畫圖我們可以發現,只要該點坐標互質即能滿足要求,由性質(1)知等價於GCD(x,y)=1;
4.由性質(4)我們知道,兩數的符號號並不影響它們的最大公約數所以對坐標取絕對值再計算;
5.注意本題有多組數據;
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int gcd(int a,int b){return b?gcd(b,a%b):a;} //GCD;
int main(){
int n,x1,x2,y1,y2,i,j,k;
scanf("%d",&n);
for(i=1;i<=n;++i){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(x1==x2&&y1==y2){ //特判,兩者坐標重合時,GCD=0,而兔子有危險;
printf("no\n");
continue;
}
x2=abs(x2-x1);
y2=abs(y2-y1);
if(gcd(x2,y2)==1)printf("no\n");
else printf("yes\n");
}
return 0;
}
3.[洛谷P2651]添加括號III
Description
現在給出一個表達式,形如a1/a2/a3/.../an;如果直接計算,就是一個個除過去,比如1/2/1/4=1/8。然而小A看到一個分數感覺很不舒服,希望通過添加一些括號使其變成一個整數。一種可行的辦法是(1/2)/(1/4)=2。現在給出這個表達式,求問是否可以通過添加一些括號改變運算順序使其成為一個整數。
輸入格式:一個測試點中會有多個表達式。第一行t,表示表達式數量。對於每個表達式,第一行是n,第二行n個數,第i個數表示ai。
輸出格式:輸出t行。對於每個表達式,如果可以通過添加括號改變順序使其變成整數,那么輸出“Yes”,否則輸出“No”
Solution
1.我們可以發現,為了使其結果盡可能為整數,我們應使分母最大,分子最小;
2.那么我們發現,a2無論如何都是在分母上的,那么我們這樣添加括號即可:a1/(a2/a3/.../an)=a1a3...*an/a2,此時滿足分母最大,分子最小;
3.那么我們需要進行約分:對每一個分子都和分母求一次GCD,每次求后令分母除以GCD,到最后一項時若分母=1,則結果為整數;
4.注意本題有多組數據;
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int gcd(int a,int b){return b?gcd(b,a%b):a;} //GCD
int main(){
int t,n,i,j;
scanf("%d",&t);
for(i=1;i<=t;++i){
scanf("%d",&n);
int a[n+1]={};
for(j=1;j<=n;++j) scanf("%d",&a[j]);
a[2]/=gcd(a[1],a[2]);
for(j=3;j<=n;++j) a[2]/=gcd(a[2],a[j]);
if(a[2]==1)printf("Yes\n");
else printf("No\n");
}
return 0;
}