Timus 1150. Digits

來源:互聯網
上載者:User
Timus 1150. Digits 要求計算 1 到 N 的正整數中包含 0 .. 9 的數目。
1150. Digits

Time Limit: 2.0 second
Memory Limit: 16 MB

John
Smith has decided to number the pages in his notebook from 1 to N.
Please, figure out the number of zeros, ones, twos, ... , nines he
might need.InputOne number N (N<1000000000)OutputThere
should be 10 lines. The first line should contain the number of zeros
needed , the second line should contain the number of ones needed, ...
, the tenth line should contain the number of nines needed.Sample
input output
12 1
5
2
1
1
1
1
1
1
1

Problem Author: Eugene Bryzgalov

Problem Source: Ural Collegiate Programming Contest, April 2001, Perm, English Tour

解答如下:

 1 using System;
 2 
 3 namespace Skyiv.Ben.Timus
 4 {
 5   // Timus 1150. Digits: 求 1 到 N 的正整數中包含 0 .. 9 的數目。
 6   // N     9  99 999 9,999 99,999 999,999 9,999,999 99,999,999 999,999,999
 7   // i*p   1  20 300 4,000 50,000 600,000 7,000,000 80,000,000 900,000,000
 8   // q     1  11 111 1,111 11,111 111,111 1,111,111 11,111,111 111,111,111
 9   // 上表中,i*p 是 1 .. 9 的數目,i*p - q 是 0 的數目。
10   // http://acm.timus.ru/problem.aspx?space=1&num=1150
11   sealed class T1150
12   {
13     static void Main()
14     {
15       int[] a = new int[10]; // 0 .. 9 的數目
16       int k = 0, n = int.Parse(Console.ReadLine());
17       for (int i = 0, m = 1, p = 1, q = 0; n > 0; i++, n /= 10, q = q * 10 + 1)
18       {
19         int d = n % 10;   //   d: n 的第 i 位元字,i: 0:個位 1:十位 2:百位 
20         k += i * p * d;   // i*p: 0 1 20 300 4000 50000 600000 7000000 
21         if (i > 0) p *= 10; // p: 1 1 10 100 1000 10000 100000 1000000 
22         for (int j = d - 1; j >= 0; j--) a[j] += p; // 0 .. d-1 的數目
23         if (n < 10) a[0] -= p + q; // q: 0 1 11 111 1111 11111 111111 
24         a[d] += m;  // d 的數目
25         m += d * p; // m: n 的低 i 位 + 1, 例 n: 59842,m: 1 3 43 843 9843
26       }
27       for (int i = 0; i < a.Length; i++) Console.WriteLine(a[i] + k);
28     }
29   }
30 }

本程式使用的演算法是非常高效的,時間複雜度是 O(logN)。主要痛點是計算 0 的數目。今天斷斷續續用了三、四個小時才完全搞定這個演算法。

是輸入 N = 59842 的。在這個例子中,i 從 0 迴圈到 4,分別對應 N 的個、十、百、千、萬位。在迴圈體內,又分為三個部分進行計數。下面以 i = 2,即 N 的百位元 d = 8 為例進行說明:

  • 程式中的第 20 行:  k += i * p * d;  對應中的淺青色部分 ( 個位沒有淺青色部分,其計數為零 )。i * p = 20,表示 00 .. 99 範圍內每種數字各出現 20 次。d = 8,表示 0 .. 7 共有 8 種 ( 中黃色部分 ),所以總計數為 i * p * d。
  • 程式中的第 22 行: for (int j = d - 1; j >= 0; j--) a[j] += p;  對應中的黃色部分。p = 100 ( 注意,這時 p 不是 10,因為程式中的第 21 行已經將 p 乘以 10 了 ),d = 8,j = 7 .. 0,表示 0 到 7 這 8 個數字各出現 100 次 ( 00 .. 99)。
  • 程式中的第 24 行: a[d] += m;  對應中的粉紅色部分。d = 8,m = 43,表示 N 的百位元字 8 共出現了 43 次 ( 00 .. 42 ),這是 N 最低兩位 ( 42 ) 加一。

至於 0 的計數,需要在最後一輪迴圈 ( N 的最高位 ) 中扣除無效前置字元為零,如程式中第 23 行: if (n < 10) a[0] -= p + q;  所示,此時 i = 4,n = 5,p = 10000,q = 1111。扣除 p 是為了中黃色部分的 0,扣除 q 是為了中淺青色部分的 0。

附:上面程式的演算法中使用了以下定理:

從 1 到 10r - 1 的正整數,包含 1 .. 9 各 r * 10r-1 個,包含 0 的數目為: r * 10r-1 - ( 100 + ... + 10r-1)。

證明如下:

以 r = 3 為例,我們來證明從 1 到 999 的正整數,包含 1 .. 9 各 300 個,包含 0 的數目為: 300 -111 = 189。

首先,考慮 000 .. 999 這 1000 個整數(暫時不丟棄前置字元為零),每個整數共包含 3 個數字,所以這 1000 個整數包含 3 * 1000 = 3000 個數字。

其次,這 1000 個整數中,0 .. 9 這 10 個數字出現的次數是相同的,也就是說, 0 .. 9 這 10 個數字各出現了 3000 / 10 = 300 次。

最後,讓我們來計算前置字元為零的個數,分為以下三種情況:

  • 000: 3 * 1 個
  • 001 .. 009: 2 * 9 個
  • 010 .. 099: 1 * 90 個

總計: 3 * 1 + 2 * 9 + 1 * 90 = 1 + (1 + 9) + (1 + 9 + 90) = 1 + 10 + 100 = 111 個。

所以,扣除前置字元為零後,包含 0 的數目為: 300 -111 = 189。

對於 r 的其他值也可以類似地證明。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.