1. 程式人生 > >矩陣翻硬幣 藍橋杯 大數開方 大數相乘

矩陣翻硬幣 藍橋杯 大數開方 大數相乘

問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣

  隨後,小明對每一個硬幣分別進行一次 Q 操作

  對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉

  其中i和j為任意使操作可行的正整數,行號和列號都是從1開始。
    // i、j為任意數
  當小明對所有硬幣都進行了一次 Q 操作後,他發現了一個奇蹟——所有硬幣均為正面朝上。

  小明想知道最開始有多少枚硬幣是反面朝上的。於是,他向他的好朋友小M尋求幫助。

  聰明的小M告訴小明,只需要對所有硬幣再進行一次Q操作,即可恢復到最開始的狀態。然而小明很懶,不願意照做。於是小明希望你給出他更好的方法。幫他計算出答案。
輸入格式   輸入資料包含一行,兩個正整數 n m,含義見題目描述。 輸出格式   輸出一個正整數,表示最開始有多少枚硬幣是反面朝上的。 樣例輸入 2 3 樣例輸出 1 資料規模和約定   對於10%的資料,n、m <= 10^3;
  對於20%的資料,n、m <= 10^7;
  對於40%的資料,n、m <= 10^15;
  對於10%的資料,n、m <= 10^1000(10的1000次方)。 思路 1. 如果一枚硬幣被翻了奇數次,那麼它原來的狀態肯定是反面朝上,所以,我們要找的就是被翻了奇數次的硬幣(立腳點) 2.  Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。 正向看可能不好想,那麼我們反向看一下,假設一個橫座標為x的硬幣,在翻哪些硬幣的時候會翻到它呢? 其實就是這個數x所有的約數,比如橫座標為4的硬幣,那麼,在翻橫座標為1,2,4的硬幣時都會翻到它,縱座標的情況是一樣的。 3.對於一個硬幣,我們必須同時考慮其橫座標x和縱座標y,假如橫座標被翻了a次,縱座標被翻了b次,則這個硬幣總共被翻了a*b
次,若想要這個硬幣被翻奇數次,a和b必須都得是奇數,即x和y都有奇數個約數 4.那麼問題來了:哪些數有奇數個約數呢?不管你知不知道,反正現在你知道了,完全平方數有奇數個約數。那麼什麼又是完全平方數呢,簡單的說就是n^2,n為自然數,也就是0,2,4,9……(平方根只有一個數,其他約數都是成對的) 5.問題又來了,怎麼求完全平方數的個數呢,首先,我們已經知道了這個矩陣式n*m的,而且是從1開始編號的,對於n,我們可以求sqrt(n),然後取整,容易想出,在1-n的範圍內的完全平方數的個數為(int)(sqrt(n))個,而sqrt(n)*sqrt(m)就是所有的橫縱座標都是完全平方數的硬幣的個數。(完全平方數:1*1、2*2、……,舉例:15,位於3*3和4*4之間,開方取整就能夠得到3)
6.下面,我們迎來了終極問題,題目思路有了,但是超大規模的資料問題並沒有得到解決,反而更麻煩了,因為居然還搞出了個開方的操作,但很不幸,這就是一道悲催的大數高精度題,而且還得自己寫出大數相乘,大數開方,大數比較等操作 首先,讓我們明確一下思路,到底如何實現乘法和開方,對於大數的儲存,我們使用string,因為它的長度比較容易控制 大數運算: 乘法:其實有很多速度快而且更巧妙的大數乘法,但是在藍橋杯,我們只要使用模擬手演算法應該是夠用,雖說是模擬,但也有一些我們常用但不太瞭解的規律,我們在手算乘法的時候,需要進行移位和進位,這兩個操作我們會通過兩步完成 1.移位:對於兩個數a=12,b=25,在相乘的時候我們讓每一位數分別相乘,即a[0]*b[0]=2 , a[0]*b[1]=5 , a[1]*b[0]=4 , a[1]*b[1]=10,那麼規律就是,對於兩個數a[i] , b[j],所有i+j相同的數(i+j相同的數單位相同)都要加到一起,所以我們要把5+4=9合併為一個數,也就是說,將每一位數相乘之後,我們實際得到了2,9,10三個沒有進位的數 2.進位:通過上面的操作,我們的2,9,10三個沒有進位的數,下面就要進行進位,因為我們是把高位相乘得到的數放在前面,低位相乘得到的數放在後面,所以這三個數排列自然也是高位在前,地位在後,所以要從右向左進位,進位的方法就是a[i+1]=a[i+1]+a[i]/10 , a[i]=a[i]%10,如果放到這三個數上面,就是,9+10/10=10,然後10%10=0,這三個數變成2,10,0,再一次就是2+10/10=3,10%10=0,三個數變為3,0,0,而這正是我們要求的結果,在實際操作中,我們習慣於將數倒著存放,即將數存為10,9,2,這是為了進位方便,因為如果正序的話如果最高位發生進位我們就要將每一個數向後移動一位從而挪出一個空位,想想都麻煩,倒序的話因為是向後進位,想怎麼進就怎麼進。所以剛才的公式改為a[i]=a[i]+a[i+1]/10(高位=高位+低位/10    低位除10得到進位數) , a[i+1]=a[i+1]%10(低位=低位%10    低位餘10得到低位數) 開方:這個開方方法不是我想出來的,是參照了網上的一個方法,我感覺挺好。實際上也可以用牛頓逼近法。 這個方法的前提:假如一個數有n位,若n為偶數,那麼這個數的方根有n/2位;若n為奇數,那麼方根為(n+1)/2位。 然後,讓我們實際看一個例子,我們假設這個數就是1200 1.很明顯,它有4位,所以它的方根有2位,然後,我們通過下面的方法來枚舉出它的整數根  00*00=0<1200     10*10=100<1200     20*20=400<1200     30*30=900<1200       40*40=1600>1200 所以,這個根的十位就是3,然後,再列舉個位                                                     31*31=961<1200 32*32=1024<1200 33*33=1089<1200 34*34=1156<1200 35*35=1225>1200 所以,這個根就是34,因為平方增長的速度還是比較快的,所以速度沒有太大問題,這裡有一個地方需要處理一下,如果一個數很長,那麼它的方根的位數也比較長,在列舉高位的時候,我們沒有必要把後面的0都加上,因為那樣會影響速度,其實0的個數完全可以算出來,然後將0的個數一起送入比較函式比較即可,這樣可以提高速度 比較:上面我們說過,比較函式接受的兩個數只有一個是完整的數,另外一個實際上是高几位的平方和0的個數,但處理方式差不多,都是先比較位數,位數大數就大,位數一樣就逐位比較,只要別忘了比較那一堆0就好了,最後其實不用把情況分的太清楚,只要返回0,1就行,因為只有當一個數大於n的時候才對我們有意義 程式碼
  1. #include<iostream>  
  2. #include<string>  
  3. #include<cstring>  
  4. using namespace std;  
  5. string strMul(string a,string b)  
  6. {  
  7.     string result="";  
  8.     int len1=a.length();  
  9.     int len2=b.length();  
  10.     int i,j;  
  11.     int num[500]={0};  
  12.     for(i=0;i<len1;i++)  
  13.     for(j=0;j<len2;j++)  
  14.     {  
  15.         num[len1-1+len2-1-i-j]=num[len1-1+len2-1-i-j]+(a[i]-'0')*(b[j]-'0');  
  16.     }  
  17.     //for(i=0;i<5;i++)  
  18.     //cout<<num[i]<<' ';  
  19.     //cout<<endl;  
  20.     for(i=0;i<len1+len2;i++)  
  21.     {  
  22.         num[i+1]=num[i+1]+num[i]/10;  
  23.         num[i]=num[i]%10;  
  24.     }  
  25.     //for(i=0;i<5;i++)  
  26.     //cout<<num[i]<<' ';  
  27.     for(i=len1+len2-1;i>=0;i--)  
  28.     {  
  29.         if(num[i]!=0)  
  30.         break;  
  31.     }  
  32.     for(;i>=0;i--)  
  33.     {  
  34.         result=result+(char)(num[i]+'0');  
  35.     }  
  36.     return result;  
  37. }  
  38. int strCmp(string a,string b,int pos)  
  39. {  
  40.     int i;  
  41.     //cout<<a<<endl;  
  42.     //cout<<a.length()<<' '<<b.length()<<endl;  
  43.     if(a.length()+pos>b.length())  
  44.     return 1;  
  45.     if(a.length()+pos<b.length())  
  46.     return 0;  
  47.     if(a.length()+pos==b.length())  
  48.     {  
  49.         for(i=0;i<a.length();i++)  
  50.         {  
  51.             if(a[i]<b[i])  
  52.             return 0;  
  53.             if(a[i]==b[i])  
  54.             continue;  
  55.             if(a[i]>b[i])  
  56.             return 1;  
  57.         }  
  58.     }  
  59. }  
  60. string strSqrt(string a)  
  61. {  
  62.     string result="";  
  63.     int i;  
  64.     int len=a.length();  
  65.     if(len%2==0)  
  66.     len=len/2;  
  67.     else  
  68.     len=len/2+1;  
  69.     for(i=0;i<len;i++)  
  70.     {  
  71.         result=result+'0';  
  72.         while(strCmp(strMul(result,result),a,2*(len-1-i))!=1)  
  73.         {  
  74.             if(result[i]==':')  
  75.             break;  
  76.             result[i]++;  
  77.         }  
  78.         result[i]--;  
  79.     }  
  80.     return result;  
  81. }  
  82. int main()  
  83. {  
  84.     string n,m;  
  85.     cin>>n>>m;  
  86.     cout<<strMul(strSqrt(n),strSqrt(m))<<endl;  
  87.     //cout<<strMul("35","35")<<endl;  
  88.     //cout<<strCmp("1024","1200",0);  
  89.     //cout<<strSqrt("9801");  
  90.     return 0;  
  91. }  

相關推薦

矩陣硬幣 藍橋 大數開方 大數相乘

問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣。  隨後,小明對每一個硬幣分別進行一次 Q 操作。  對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。  其中i和j為任意使操作可行的正整數,行號和列號都是從1開始

藍橋矩陣硬幣大數開根號)

  對於10%的資料,n、m <= 10^3;  對於20%的資料,n、m <= 10^7;  對於40%的資料,n、m <= 10^15;  對於10%的資料,n、m <= 10^1000(10的1000次方)。 我的思路:他是問翻之前有多少個硬幣是反面朝上的,所以這個反面朝上的

歷屆試題 矩陣硬幣 【java大數開方

  歷屆試題 矩陣翻硬幣   時間限制:1.0s   記憶體限制:256.0MB 問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣。   隨後,小明對每一個硬幣分別進行一次 Q 操作。   對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第

藍橋 矩陣硬幣(打表+二分)

網上其他人的答案好像都是用數學方法解決的。做這道題的時候一看就感覺是找規律的題,所以先打個表。因為後面要用到大數處理,所以是Java語言打表程式碼(就是按題目意思暴力):import java.io.FileNotFoundException; import java.io.

藍橋題解】矩陣硬幣

歷屆試題 矩陣翻硬幣 時間限制:1.0s 記憶體限制:256.0MB 提交此題 問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣。   隨後,小明對每一個硬幣分別進行一次 Q 操作。   對第x行第y列的硬幣進行 Q 操作的定義:將所有

藍橋_PREV-34_矩陣硬幣

問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣。隨後,小明對每一個硬幣分別進行一次 Q 操作。對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。其中i和j為任意使操作可行的正整數,行號和列號

藍橋 矩陣硬幣

article compare 畫蛇添足 equals http 等於 最大值 行號 兩個 矩陣翻硬幣 本文轉自 https://blog.csdn.net/xiaofengcanyuelong/article/details/79255105 小明先把硬幣擺成了一個

藍橋-矩陣硬幣

  小明先把硬幣擺成了一個 n 行 m 列的矩陣。   隨後,小明對每一個硬幣分別進行一次 Q 操作。   對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。   其中i和j為任意使操作可行的正整數,行號和列號都是從1開始。   當小明對所有硬幣都進行了一次 Q

藍橋歷屆試題----矩陣硬幣

矩陣翻硬幣問題描述小明先把硬幣擺成了一個 n 行 m 列的矩陣。隨後,小明對每一個硬幣分別進行一次 Q 操作。對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。其中i和j為任意使操作可行的正整數,行號和列號都是從1開始。當小明對所有

矩陣硬幣 數學 藍橋

    小明先把硬幣擺成了一個 n 行 m列的矩陣。隨後,小明對每一個硬幣分別進行一次 Q 操作。對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y列的硬幣進行翻轉。其中i和j為任意使操作可行的正整數,行號和列號都是從1開始。    當小明對所有硬幣都進行了一次 Q操作後,他發現了一個

藍橋 演算法提高 大數加法

  演算法提高 大數加法   時間限制:1.0s   記憶體限制:256.0MB 問題描述   輸入兩個正整數a,b,輸出a+b的值。 輸入格式   兩行,第一行a,第二行b。a和

矩陣硬幣

man 得出 ons 過大 字符 blog 過程 image 什麽 OJ鏈接:http://lx.lanqiao.cn/problem.page?gpid=T126 如果能理解其內在的含義,可以得40分保命: 保命代碼: #include <stdio.h>

矩陣硬幣(C++)

問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣。   隨後,小明對每一個硬幣分別進行一次 Q 操作。   對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。   其中i和j為任意使操作可行的正整數,

歷屆試題 矩陣硬幣

 歷屆試題 矩陣翻硬幣   時間限制:1.0s   記憶體限制:256.0MB 問題描述   小明先把硬幣擺成了一個 n 行 m 列的矩陣。   隨後,小明對每一個硬幣分別進行一次 Q 操作。   對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x

歷屆試題 矩陣硬幣

  小明先把硬幣擺成了一個 n 行 m 列的矩陣。   隨後,小明對每一個硬幣分別進行一次 Q 操作。   對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。   其中i和j為任意使操作可行的正整數,行號和列號都是從1開始。   當小明對所有硬幣都進行了一次 Q

第四屆藍橋第八題 硬幣

i++ nbsp ans namespace == pos else main 硬幣 題解:簡單貪心, 比賽之前寫寫水題 #include <iostream> #include <cstring> #include <strin

藍橋 硬幣 貪心

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

藍橋 演算法訓練 區間k大數查詢

明明是一個水題卻wrong answer了很多遍。。 還是因為對最基礎的氣泡排序概念不清。蠢死算了。 這下終於搞清楚了。 問題描述 給定一個序列,每次詢問序列中第l個數到第r個數中第K大的數是哪個。 輸入格式 第一行包含一個數

[Java] 藍橋ADV-136 演算法提高 大數加法

問題描述輸入兩個正整數a,b,輸出a+b的值。輸入格式兩行,第一行a,第二行b。a和b的長度均小於1000位。輸出格式一行,a+b的值。樣例輸入4 2樣例輸出6 package adv136; im

藍橋演算法題庫 區間k大數查詢

藍橋杯演算法題庫 區間k大數查詢 題目 解題思路 題目 問題描述 給定一個序列,每次詢問序列中第l個數到第r個數中第K大的數是哪個。 輸入格式 第一行包含一個數n,表示序列長度。 第二行包含n個正整數,表示給定的