問題:
給定一個十進制整數N,寫下從1開始到N的所有數字,然後數一下其中1的個數。
例如N = 16,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
其中 1 的個數爲 9 個。
分析:
第一種方式比較暴力, 一個數字一個數字的查。複雜度爲 N*(log2N)
第二種方式找規律,如例子中N=16的情況來看:
- 第一個是個位上的 1: 1, 11 兩個
- 第二個是十位上的 1: 10, 11, 12,13,14,15,16 七個
1: 9 個
再如N = 23
- 第一個是個位上的 1: 1, 11,21 三個
- 第二個是十位上的 1: 10 ~ 19 十個
1: 13 個
所以如果 十位上遞增了一個,那麼個位上的1便會多加一個; 十位數決定個位數
如果十位上的數是一個1,或大於1,那麼個位數有多少個,便決定十位上1的個數。
再如 N = 123
- 第一個是個位上的 1: 1, 11,21...121 0, 10 ,20, .... 120 13個
- 第二個是十位上的 1: 10 ~ 19, 110 ~ 119 20個
- 第三個是百位上的 1: 100 ~ 123, 24個
1:13 + 20 + 24= 57
按照位數分析,每位上的1和它的高位和低位都有關係。 假設當前位 : c 高位: h 低位的數值: l step:10的進步
if (c == 1) // 1 的個數和高位和低位都有關係
cnt += h*(step/10) + l + 1
else if (c == 0) // 1 的個數和高位有關係
cnt += h*(step/10)
else // 當前位大於 1 的情況,和高位有關係
cnt += (h+1)*(step/10) +1
實例程序
#include <iostream>
using namespace std;
int calc(int N)
{
int nCnt = 0;
int nH=0, nC=0, nL=0;
int nStep = 10;
cout << "N=" << N << endl << endl;
while(nStep/10 <= N)
{
nC = (N % nStep)/(nStep/10);
nH = N / nStep;
nL = (N % nStep) - nC * (nStep/10);
cout << "C=" << nC << " H=" << nH << " L=" << nL << " Step=" << nStep/10 << endl;
switch(nC)
{
case 0:
nCnt += nH*(nStep/10);
break;
case 1:
nCnt += nH*(nStep/10) + nL+1;
break;
default:
nCnt += (nH+1)*(nStep/10);
break;
}
nStep *= 10;
}
cout << "count=" << nCnt << endl;
cout << "=====================================\n" <<endl;
return nCnt;
}
int main()
{
int nRet = 0, i = 0;
int test[] = {19,0,1,10,23,123,223,1023};
int len = sizeof(test)/sizeof(test[0]);
for (i = 0; i < len; i++)
{
nRet = calc(test[i]);
}
cin >> nRet;
return 0;
}
擴展問題
二進制數,有下面的規律
f(1) = 1 (1有一個 1)
f(10) = 10 (01, 10 有2個 1)
f(11) = 100 (01 10 11 有4個 1)
函數f()的作用就是求二進制數中有多少個 1存在。
擴展問題分析
可以先從全 1 的情況開始分析:
f(1) = 1 (有1位 k = 1)
f(11) = 4 (有2位 k = 2)
f(111) = 12 (有3位 k = 3)
所以可以得到全 1 時的公式 k * 2^(k-1) k是一共有多少位數
那麼一個二進制數 1 的個數可以拆成兩個部分,去掉最高位 1 以後,剩下(k-1)位的全1的個數 + 加上最高位的1後的剩餘個數
例如:
10010 就可以分成兩個部分 1111 的1的和 + 從10000 到 10010 的1的和
從10000 到 10010 還可以繼續拆分:
- 10010 最高位的1的個數是由後面的低位決定的 比如10010-->
10000, 10001,10010 紅色的部分有三個。
- 0010 低位還需要繼續按照上面的規則迭代,直到結束 0001 00010 兩個 同 f(10)
擴展問題代碼:
#include <iostream>
using namespace std;
/*
first calculate the count of 1 in a full 1 number
eg: 1 => cnt = 1; 11 => (01 10 11) cnt = 4; 111 => (001 010 011 100 101 110 111) cnt = 12
so we got cnt = k * 2^(k-1)
*/
int calc(char* binary, int len)
{
int nCnt = 1, num = 0;
int k = len, total = 0;
char* p = binary+1;
if (len <= 0)
return 0;
while (k > 1)
{
if (binary[k-1]=='1')
num += nCnt;
nCnt *= 2;
k--;
}
if (num == (nCnt-1)) // full of 1
return total = len*nCnt;
else
total = (len-1)*(nCnt/2);
total += num+1;
k = 1;
while ((*p != '1') && (k<len))
{
*p++; k++;
}
total += calc(p, len-k);
return total;
}
int main()
{
char* test[]= {"100", "1", "10", "11", "111",
"10000", "1111", "10110", "110011", "110011001"};
int test_num = 10;
int len = 0, i = 0, total=0;
for (i = 0; i < test_num; i++)
{
len = strlen(test[i]);
total = calc(test[i], len);
cout << test[i] << ": result=" << total << endl;
cout << "\n\n =========================\n" << endl;
}
cin >> len;
return 0;
}