【幸運數】求解過程
原題目:
小明同學學習了不同的進位制之後,拿起了一些數字做起了遊戲。小明同學知道,在日常生活中我們最常用的是十進位制數,而在計算機中,二進位制數也很常用。現在對於一個數字x,小明同學定義出了兩個函式f(x)和g(x)。 f(x)表示把x這個數用十進位制寫出後各個數位上的數字之和。如f(123)=1+2+3=6。 g(x)表示把x這個數用二進位制寫出後各個數位上的數字之和。如123的二進位制表示為1111011,那麼,g(123)=1+1+1+1+0+1+1=6。 小明同學發現對於一些正整數x滿足f(x)=g(x),他把這種數稱為幸運數,現在他想知道,小於等於n的幸運數有多少個?
輸入描述:每組資料輸入一個數n(n<=100000)
輸出描述:每組資料輸出一行,小於等於n的幸運數個數。
當輸入21時,輸出3,因為在[1,21]的區間內,只有1,20,21這三個數字的十進位制和和二進位制和相等。
【解題思路】
如果按照題目描述編寫兩個函式,對給出區間內所有的數字進行二進位制和十進位制和分別求解,然後在做比較,當給出的數字很大時,在題目規定時間內完成是不可能的,常規思路題目給出不可用,那麼只能從十進位制和二進位制兩種不同進位制下,各數位上數字之和尋找規律,通過某種規律將100000以內的數位和保留建表,通過查表對比的方式,確定其小於等於n的幸運數個數。由於預設最大值為100000,對此開闢了大小為100001的陣列對其數位和進行儲存。
以二進位制為例,其前16個數字的數位和如下圖所示:
可以發現,每次二進位制向前進一位,其後面所有數位又一次重複進位前的所有數字的過程,例如,當N=2時,二進位制為10,保持第一個進位制位不變,則後面只有一個進位制位在改變,改變總數是兩個,即10,11。在2之前,有0和1兩個數字,則在10改變至11的過程中(從2變化為3),至少在0和1的基礎上加1即可。同理,當N=4時,其二進位制為100,從100變化到111(即4到7)的過程中一共有4個,去其第一個進位制位,後面兩個進位制位的變化過程為00,01,10,11,即為0到3的變化過程。以此類推,可以將所求範圍內的所有數字的二進位制數位和求出,其程式碼如下(注意邊界問題)
vector<int> datatwo(N, 0 );
datatwo[1] = 1;
int index = 2;
while (true)
{
for (int i = 0; i < index; i++)
{
if (index + i < N)
datatwo[index + i] = datatwo[i] + 1;
else
{
index = N;
break;
}
}
if (index >= N)
break;
else
index *= 2;
}
對於十進位制上的進位制位之和的求法,與二進位制類似,只不過在首位進位制位上,由於二進位制下要麼是0,要麼是1,所以對於首位後僅僅需要重複前面一次即可完成。但對於十進位制則不同。十進位制下數位和求解程式碼如下所示:
vector<int> dataten(N, 0);
for (int i = 0; i < 10; i++)
dataten[i] = i;
index = 10;
int cnt = 10;
while (index < N - 2)
{
for (int j = 0; j < 9; j++)
for (int k = 0; k < cnt; k++)
dataten[index + cnt * j + k] = dataten[k] + 1 + j;
cnt *= 10;
index = cnt;
}
dataten[index] = 1;
當數字是個位數時,則其數位和為其本身,保留寫進表中(注:從0開始到9,一共10個數字)。當數位為兩位數時,對於每個十位上的數字,其對應個位上數字上將變化10次。例如:當十位上數字為1時,其個位上數字從0變化到9,其數位和分別為數字0–9數位和加1的結果。當十位上數字為2時,其個位上數字從0變化到9,其數位和分別為數字0–9數位和加2的結果,以此類推,在10到99的變化過程中,一共需要迴圈個位數9次,每次按照十位上數字的不同,累加不同的值。當數字是三位數時,規律如兩位數相同。通過對數字區間劃分,保留百位上數字不變時,迴圈中的不再僅僅是10個個位數,而是小於最小三位數(即100)100個數字,即100-199,200-299等,直到900-999。邊界上,預設值為100001,當index為99999時,退出迴圈,並對dataten[100000]賦值為1。
驗證:每次對於輸入的數字,只需要判斷從1到該數字上,這一段區間上,兩個陣列對應位置上的數位和是否相同即可,並累加其總數和。
int sum = 0;
for (int i = 1; i <= n; i++)
if (dataten[i] == datatwo[i])
{
cout << i << endl;
sum++;
}