問題描述
我們知道,整數做除法時,有時得到有限小數,有時得到無限循環小數。
如果我們把有限小數的末尾加上無限多個0,它們就有了統一的形式。
本題的任務是:在上面的約定下,求整數除法小數點后的第n位開始的3位數。
如果我們把有限小數的末尾加上無限多個0,它們就有了統一的形式。
本題的任務是:在上面的約定下,求整數除法小數點后的第n位開始的3位數。
輸入格式
一行三個整數:a b n,用空格分開。a是被除數,b是除數,n是所求的小數后位置(0<a,b,n<1000000000)
輸出格式
一行3位數字,表示:a除以b,小數后第n位開始的3位數字。
樣例輸入
1 8 1
樣例輸出
125
樣例輸入
1 8 3
樣例輸出
500
樣例輸入
282866 999000 6
樣例輸出
914
思路:我們以3/7舉例,下面是手寫的常規除法的計算過程。
比如n=4時,我們計算的是小數第四位,那么很明顯就是40除於7的值,所以我們只需要知道第四位小數對應的哪個被除數是誰就行,用變量sa表示被除數,則第一位小數就是sa/7,余數就是下一個小數對應的被除數,那么用余數更新sa,一次類推可以計算出所有的小數,注意上圖第一個被除數30如果在下面再次出現則容易知道接下來會重復計算,意味着產生循環節,所以我們可以通過循環節長度更新n,降低時間復雜度。
代碼
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int main() 6 { 7 int a,b,n; 8 int sa; 9 cin >> a >> b >> n; 10 sa=a%b;//初始化sa,sa是求每次相除的余數 11 for(int i=1;i<=n;i++){ 12 sa=sa%b*10;//在本輪循環相除后余數發生改變 13 //cout << sa <<" "<<sa%b <<" "<<a%b << endl; 14 if(sa%b==a%b){//如果下一次循環(注意sa%b是下一次循環的余數)的余數 等於初始的余數,說明接下來的循環或重復之前的計算 15 n%=i;//n縮小(這是降低時間復雜度的關鍵) , 16 i=0;//重新開始遍歷 17 } 18 } 19 for(int i=1;i<=3;i++){ 20 cout << sa/b;//輸出該位置計算結果 ,注意如果是有限小數切sa=0時,接下來都是輸出0 21 sa=sa%b*10;//下一次計算的余數 22 } 23 return 0; 24 }
但是仔細一想上個代碼其實是不完全正確的,或不完美的,雖然可以通過測試系統,因為上個代碼默認循環節的第一個數字就是小數點后的第一個數字,但是很許多循環小數並非如此
這是第三個測試用例相除的結果,發現循環節是149,從第四個數字開始,如果我們要查詢結果數億之后的某個數的后三位,上個代碼明顯超時,所以需考慮循環節不在第一位的情況,
在上個代碼上修改如下
1 #include<bits/stdc++.h> 2 using namespace std; 3 int t=1; 4 int array[10]; 5 int compare(int m)//得到循環節開始的下標 6 { 7 for(int i=1;i<t;i++){ 8 if(array[i]==m) return i;//此時第二個循環節的第一個數字遇到第一個循環節的第一個數字,返回第一個循環節開始的下標 9 10 } 11 return 0; 12 } 13 int main() 14 { 15 int a,b,n; 16 int sa; 17 cin >> a >> b >> n; 18 int m=0;//表示還沒遇到循環節 19 int num=0; 20 21 memset(array,0,sizeof(array)); 22 sa=a%b;//初始化sa,sa是求每次相除的余數 23 for(int i=1;i<=n;i++){ 24 if(m==0 && i!=1) array[t++]=sa%b; 25 if(m==0 && i==1) array[t++]=sa; 26 sa=sa%b*10;//在本輪循環相除后余數發生改變 27 if(m==0) 28 { 29 int aa=sa%b; 30 num=compare(aa); 31 // cout << num << endl; 32 } 33 if(num!=0&&m!=1){ 34 n%=(i-num+1); 35 n+=(num-1); 36 i=0; 37 m=1; 38 } 39 //if(nu) 40 } 41 for(int i=1;i<=3;i++){ 42 cout << sa/b;//輸出該位置計算結果 ,注意如果是有限小數切sa=0時,接下來都是輸出0 43 sa=sa%b*10;//下一次計算的余數 44 } 45 return 0; 46 }
下面的小demo是計算循環節的,根據上個代碼衍生出來的
1 #include<bits/stdc++.h> 2 using namespace std; 3 int t=1; 4 int array[100]; 5 int compare(int m)//得到循環節開始的下標 6 { 7 for(int i=1;i<t;i++){ 8 if(array[i]==m) return i;//此時第二個循環節的第一個數字遇到第一個循環節的第一個數字,返回第一個循環節開始的下標 9 10 } 11 return 0; 12 } 13 int main() 14 { 15 //freopen("D:/Data.txt","r",stdin); 16 int a,b; 17 int sa; 18 cin >> a >> b; 19 int num=0; 20 memset(array,0,sizeof(array)); 21 sa=a%b; 22 for(int i=1;i<1000;i++){ 23 if(sa%b==0) {t=1000; break;} 24 if(i!=1) array[t++]=sa%b; 25 if(i==1) array[t++]=sa; 26 sa=sa%b*10; 27 int aa=sa%b; 28 num=compare(aa); 29 if(num!=0) break; 30 31 } 32 if(t==1000) cout << "不存在循環節" << endl; 33 else{ 34 sa=a%b; 35 for(int i=1;i<=t;i++){ 36 if(i>num) cout << sa/b;//輸出該位置計算結果 ,注意如果是有限小數切sa=0時,接下來都是輸出0 37 sa=sa%b*10;//下一次計算的余數 38 } 39 } 40 return 0; 41 }