包含一

題目描述

  NowCoder老是力爭上游,凡事都要拿第一,因此他對「1」這個數情有獨鍾。愛屋及烏,他也很喜歡包含1的數,例如十、十一、12……。你能幫他統計一下有多少個包含1的正整數嗎?java

1.1 輸入描述:

  輸入有多組數據,每組數據包含一個正整數n,(1≤n≤2147483647)。算法

1.2 輸出描述:

  對應每組輸入,輸出從1到n(包含1和n)之間包含數字1的正整數的個數。測試

1.3 輸入例子:

1
9
10
20

 

1.4 輸出例子:

 

1
1
2
11

 

2 解題思路

 

  假設有數字X(n)=xnxn−1…x2x1x0,xi上的權重是10i
  先考慮0~9n9n−1…929190,中出現1的數字個數,假設它是P(n),它由三部分組成:
  - 0~9n−1…929190,含有1的數字數目是P(n−1)
  - 1n0n−1…929190~1n9n−1…929190,含有1的數字數目是10n−1
  - 1<ji<xiji0i−1…020100 ji9i−1…929190中含有1的數字數目是P(n−1),ji能夠取8個數字。
  因此P(n−1)=P(n−1)+10n−1+8∗P(n−1)=9∗P(n−1)+10n−1,又n=0時,P(n)=1,綜上有:spa

 

P(n)={19∗P(n−1)+10n−1n=0n>0code

 

  再考慮X(n),從右到左處理X(n)上的每一位,假設當前處理第i位。則要分三種狀況:
  第一種:xi=0,則xixi−1…x2x1x0與xi−1…x2x1x0含有1的數字個數相同,則F(i)=F(i−1)。
  第二種:xi=1,則xixi−1…x2x1x0包含1的由兩部分組成:
    - 0~9i−1…929190中含有1的數字數,爲P(i−1)
    - xi0i−1…020100~xixi−1…x2x1x0中含有1的數字,爲X(i−1)+1
  則有F(i)=P(i−1)+X(i−1)+1
  第三種:xi>1,則xixi−1…x2x1x0包含1的由四部分組成:
    - 0~xi−1…x2x1x0中含有1的數字數,爲P(i−1)
    - 1i0i−1…020100~1i9i−1…929190中含有1的數字數,爲10i−1
    - 1<ji<xiji0i−1…020100~ji9i−1…929190中含有1的數字數,爲P(i−1)。j_i能夠取xi−2個
    - xi0i−1…020100 xixi−1…x2x1x0中含有1的數字,爲F(i−1)
  則有F(i−1)=P(i−1)+10i−1+(xi−2)P(i−1)+F(i−1)=(xi−1)P(i−1)+10i−1+F(i−1)
  綜合有:
  當n=0時,X(n)=x0圖片

 

F(n)=1get

 

  當n>0時,X(n)=xnxn−1…x2x1x0io

 

F(n)=⎧⎩⎨F(n−1)P(n−1)+X(n−1)+1(xn−1)P(n−1)+10n−1+F(n−1)xn=0xn=1xn>1for循環

 

3 算法實現

import java.util.Scanner;

/**
 * Declaration: All Rights Reserved !!!
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            System.out.println(countOne(n));
        }

        scanner.close();
    }

    /**
     * 【方法一】
     * 計算[1-n]中包含數字1的數字個數
     *
     * @param n 最在範圍
     * @return 包含數字1的數字個數
     */
    private static int countOne(int n) {

        int countedN = 0;
        int result = 0;

        // 從右向左分析n的每一位;for循環中:i 表示分析到了哪一位,i=1表示個位,i=10表示十位,以此類推;
        // onesPerI 表示從0到i-1含有1的數的個數,0,1,19 ...;
        // cur 是目前分析的那一位的數值;
        // 舉個例子: f(m,n) 表示從m到n,含有1的數字的個數。
        // f(1,500) = f(1, 99)+f(100, 199)+f(200, 299)+(300, 399)+f(400, 499)
        // f(1, 99) = f(200, 299) = f(300, 399) = f(400, 499)
        // f(100, 199) = 100
        for (int i = 1, onesPerI = 0, cur; n != 0; onesPerI = onesPerI * 9 + i, i *= 10, n /= 10) {

            // 當前數位的數值
            cur = n % 10;

            if (cur == 0) {
                continue;
            } else if (cur == 1) {
                // onesPerI表示[1, i-1]含有1的個數,countedN表示比
                result = onesPerI + countedN + 1;
            } else {
                result += (cur - 1) * onesPerI + i;
            }

            // 表示比第i位以及比第i位低的各位的數值,好比abcdef,如今處理萬位,那麼countN就是bcdef
            countedN += cur * i;

        }

        return result;
    }

}

 

4 測試結果

 

這裏寫圖片描述

相關文章
相關標籤/搜索