統計0到n之間1的個數[數學,動態規劃dp](經典,詳解)

轉載自https://www.cnblogs.com/ECJTUACM-873284962/p/6658711.html

問題描述

給定一個十進制整數N,求出從1到N的所有整數中出現”1”的個數。 

例如:N=2時 1,2出現了1個 “1” 。

N=12時 1,2,3,4,5,6,7,8,9,10,11,12。出現了5個“1”。

方法一 暴力求解

最直接的方法就是從1開始遍歷到N,將其中每一個數中含有“1”的個數加起來,就得到了問題的解。

下面給出代碼:

複製代碼
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 int main()
 5 {
 6     int n,x,t;
 7     while(scanf("%d",&n)!=EOF)
 8     {
 9         int ans=0;
10         for(int i=1;i<=n;i++)
11         {
12             t=i;
13             while(t)
14             {
15                 if(t%10==1)
16                     ++ans;
17                 t=t/10;
18             }
19         }
20         printf("%d\n",ans);
21     }
22     return 0;
23 }
複製代碼

該算法的時間複雜度爲O(N*lgN)

(注:此方法對較大的數據有可能會TL)

解法二 

1位數的情況:

在解法二中已經分析過,大於等於1的時候,有1個,小於1就沒有。

 2位數的情況:

N=13,個位數出現的1的次數爲2,分別爲1和11,十位數出現1的次數爲4,分別爲10,11,12,13,所以f(N) = 2+4。

N=23,個位數出現的1的次數爲3,分別爲1,11,21,十位數出現1的次數爲10,分別爲10~19,f(N)=3+10。

由此我們發現,個位數出現1的次數不僅和個位數有關,和十位數也有關,如果個位數大於等於1,則個位數出現1的次數爲十位數的數字加1;如果個位數爲0,個位數出現1的次數等於十位數數字。而十位數上出現1的次數也不僅和十位數相關,也和個位數相關:如果十位數字等於1,則十位數上出現1的次數爲個位數的數字加1,假如十位數大於1,則十位數上出現1的次數爲10。

 3位數的情況:

N=123

個位出現1的個數爲13:1,11,21,…,91,101,111,121

十位出現1的個數爲20:10~19,110~119

百位出現1的個數爲24:100~123

 我們可以繼續分析4位數,5位數,推導出下面一般情況: 

假設N,我們要計算百位上出現1的次數,將由三部分決定:百位上的數字,百位以上的數字,百位一下的數字。

如果百位上的數字爲0,則百位上出現1的次數僅由更高位決定,比如12013,百位出現1的情況爲100~199,1100~1199,2100~2199,…,11100~11199,共1200個。等於更高位數字乘以當前位數,即12 * 100。

如果百位上的數字大於1,則百位上出現1的次數僅由更高位決定,比如12213,百位出現1的情況爲100~199,1100~1199,2100~2199,…,11100~11199,12100~12199共1300個。等於更高位數字加1乘以當前位數,即(12 + 1)*100。

        如果百位上的數字爲1,則百位上出現1的次數不僅受更高位影響,還受低位影響。例如12113,受高位影響出現1的情況:100~199,1100~1199,2100~2199,…,11100~11199,共1200個,但它還受低位影響,出現1的情況是12100~12113,共114個,等於低位數字113+1。

綜合以上分析,寫出如下代碼:

複製代碼
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<map>
 4 #include<cstring>
 5 #include<string>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<vector>
 9 #include<stack>
10 #include<cstdlib>
11 #include<cctype>
12 #include<cmath>
13 #define LL long long
14 using namespace std;
15 int CountOne(int n) {
16     int cnt = 0;
17     int i = 1;
18     int current = 0, after = 0, before = 0;
19     while ((n / i) != 0) {
20         current = (n / i) % 10;
21         before = n / (i * 10);
22         after = n - (n / i) * i;
23         if (current > 1)
24             cnt = cnt + (before + 1) * i;
25         else if (current == 0)
26             cnt = cnt + before * i;
27         else if (current == 1)
28             cnt = cnt + before * i + after + 1;
29             i = i * 10;
30     }
31     return cnt;
32 }
33 int main()
34 {
35     int n;
36     while(cin>>n){
37         int res=CountOne(n);
38         cout<<res<<endl;
39     }
40     return 0;
41 }
複製代碼

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章