C語言程序設計100例之(52):數字序列


例52  數字序列

問題描述

給出了一個正整數i。編寫一個程序,以查找數字序列S1S2…Sk中位於位置i的數字。每組Sk由一系列從1到k的正整數組成。

例如,序列的前80位數字如下所示:

11212312341234512345612345671234567812345678912345678910123456789101112345678910

輸入

輸入文件的第一行包含一個整數t(1≤T≤10),測試用例的數量。

每個測試用例占一行,該行包含一個整數i(1≤i≤ 2147483647)

輸出

每個測試用例輸出一行,其中包含位於位置i的數字。

輸入樣例

2

8

3

輸出樣例

2

2

        (1)編程思路。

        先將整個數字序列112123123412345123456123456712345678123456789…分組如下

        1  12  123  1234  12345  123456  1234567  12345678  123456789…

        設a[i] 表示第i組數字序列的長度。顯然有

        a[1] = 1, a[2] =2, a[3] = 3, …,a[9]=9,a[10]=11,a[11]=13,…。

        通過對比a[i]與a[i+1]發現,當i<9時,a[i+1]=a[i]+1;當10<i<99時,a[i+1]=a[i]+2;

        當100<i<999時,a[i+1]=a[i]+3;…,依次類推。

        用如下的循環可以求得第n個位置的數字應該在第i組中的位置值pos

          for (i=1, pos=n; a[i]<pos; i++)

               pos=pos-a[i];

        然后求出pos位置值的數字是第i組中的第j個數,並且確定其是第j個數的第幾位,從而得到答案。

        上面定義的a數組還可用於確定pos位置的數字在某組中的哪一個數中,此時a[i]可表示前i個數的總長度(位數),a[1]=1表示1的長度為1,a[2]=2表示到第2個數的長度為2,即12的位數為2,…,a[11]=13表示到第11個數11的總長度為13,即1234567891011的位數為13。

        例如,要求序列第14個數是多少?

        14-a[1]-a[2]-a[3]-a[4]=4<a[5],因此,第14個數字應該出現在第5組中,且位置值為4,它是第5組的第4個數的第1個數字,所以第14個數字是4。

        再如,要求序列第100個數是多少?

        100-a[1]-a[2]-a[3]-a[4]-a[3]-a[4]-a[5]-a[6]-a[7]-a[8]-a[9]-a[10]-a[11]-a[12]

         =100-(1+2+3+4+5+6+7+8+9+11+13+15) = 16 < a[13],因此,第100個數字應該出現在第13組中,且在該組的位置值為16,而a[12]=15,a[13]=17,因此位置值16的數字應該出現在第13組的第13個數中,並且是第13個數的第16-15=1位,而第13個數13的第1位為1,所以第100個數字是1。

(2)源程序。

#include <stdio.h>

int main()

{

    int  a[31269];  // a[i] 表示第i組數字序列的長度

    int x=1,y=1;

    int i,j;

    for (i=1;i<=31268;i++)

       {

        a[i]=x;

        if (i==9||i==99||i==999||i==9999) y++;

        x+=y;

    }

    int t,n;

    scanf("%d",&t);

    while (t--)

       {

        scanf("%d",&n);

        int pos,index,digit[5],len;

           // 求得第n個位置在第i組中的下標值pos

        for (i=1, pos=n; a[i]<pos; i++)

               pos=pos-a[i];

        j=1;

           // pos位置值的數字是第i組中的第j個數字

        while (a[j]<pos) j++;

        // 第n個位置的數字是數j的第index位

        index=pos-a[j-1];

           len=0;

        while(j)

           {

           digit[len++]=j%10;

           j/=10;

        }

        printf("%d\n",digit[len-index]);

    }

    return 0;

}

習題52

52-1  第N個數字

問題描述

假設:

S1 = 1

S2 = 12

S3 = 123

S4 = 1234

……

S9 = 123456789

S10 = 1234567891

S11 = 12345678912

……

S18 = 123456789123456789

S19 = 1234567891234567891

……

現在我們把所有的串連接起來

S = 1121231234……123456789123456789112345678912……

那么你能告訴我在S串中的第N個數字是多少嗎?

輸入

輸入首先是一個數字T,代表有T次詢問。

接下來的T行每行有一個整數N(1 <= N < 2^31)。

輸出

對於每個N,輸出S中第N個對應的數字。

輸入樣例

6

1

2

3

4

5

10

輸出樣例

1

1

2

1

2

4

        (1)編程思路。

        由於數字序列中只是數字1~9循環,因此本題比例52簡單。

        設a[i] 表示第i組數字序列的長度。顯然有 a[i]=i。先求出第n個數字在第i組,將前面各組的數字個數都減掉,剩余的數就是在第i組的位置,再對這個位置除9取余即可。

        (2)源程序。

#include <stdio.h>

int main()

{

    int t;

    scanf("%d",&t);

    while (t--)

    {

        int n;

        scanf("%d",&n);

        int a=1;   // 第1組1個數

        while (n>a)

        {

          n-=a;    // 減去上面每組的數字個數

          a++;

        }

        n=n%9;

        if(n==0) n=9;

        printf("%d\n",n);

    }

    return 0;

}

52-2  第M個整數

問題描述

將1~n這n個正整數按字典順序進行排序。字典排序的含義為:從最高位開始比較。1開頭的數排在最前面,然后是2開頭的數,然后是3開頭的數,……最高位相同的數字,按同樣的邏輯比較次高位,……,以此類推。

給定一個整數m,返回排序后第m個整數是多少?

例:給定整數為n=13,m=5,那么字典排序結果為:1,10,11,12,13,2,3,4,5,6,7,8,9,序列中第5個整數為13。

輸入

輸入包括多行,每行是兩個整數N和M(1≤M≤N≤106)。

輸出

對每行輸入,輸出一行,該行是第M個整數。

輸入樣例

11 4

200 25

100000000888888879 1000000000

輸出樣例

2

120

100000001

        (1)編程思路。

先觀察一下,若n=210,按字典序排列依次為

1 10 100 101 102 103 104 105 106 107 108 109

11 110 111 112 113 114 115 116 117 118 119

12 120 121 122 123 124 125 126 127 128 129

13 130 131 132 133 134 135 136 137 138 139

14 140 141 142 143 144 145 146 147 148 149

15 150 151 152 153 154 155 156 157 158 159

16 160 161 162 163 164 165 166 167 168 169

17 170 171 172 173 174 175 176 177 178 179

18 180 181 182 183 184 185 186 187 188 189

19 190 191 192 193 194 195 196 197 198 199

2 20 200 201 202 203 204 205 206 207 208 209

21 210 22 23 24 25 26 27 28 29

3 30 31 32 33 34 35 36 37 38 39 4 40 41 42 43 44 45 46 47 48 49

5 50 51 52 53 54 55 56 57 58 59 6 60 61 62 63 64 65 66 67 68 69

7 70 71 72 73 74 75 76 77 78 79 8 80 81 82 83 84 85 86 87 88 89

9 90 91 92 93 94 95 96 97 98 99

        定義b[i]保存位數不超過i的整數的個數。顯然,b[1]=10(即0~9這10個),b[2]=110(即1位的0~9,2位的00~99),b[3]=1110,…,b[10]=11111111110,…。這個數字的作用在於確定以某個數字k開頭的不同位數的個數有多少。例如,要確定以數字1開頭的長度不超過4位的整數有多少個?首先,整數1肯定是一個,置cnt=1,以后可以看成在1的后面依次擴充1位、擴充2位和擴充3位得到,cnt=cnt+b[3]=1111,即以數字1開頭的長度不超過4位的整數有1111個。再例如,要確定以數字1開頭的長度不超過4位且小於1234的整數有多少個?首先,整數1肯定是一個,置cnt=1,以后可以看成在1的后面依次擴充1位、擴充2位和擴充3位得到,但擴充3位后得到的4位數可能超過1234,而擴充1位或2位得到的2位數或3位數肯定小於1234,所以cnt=cnt+b[2]=111(擴充1~2位),再計算擴充3位的情況,cnt=cnt+(1234%1000)+1=346,即以數字1開頭的長度不超過4位且小於1234的整數有346個。若要確定以數字2開頭的長度不超過4位且小於1234的整數,則cnt=1,cnt=cnt+b[2]=111,計算擴充3位時,由於2>1234/1000,即以2開頭的4位數均大於1234,這樣以數字2開頭的長度不超過4位且小於1234的整數只有111個。

        求按字典序排列的第M個數時,從高位到低位,每位從小到大進行枚舉填充數字,枚舉時按上面的介紹計算以數字k開頭的長度不超過len位且值不超過N的整數的個數,從而確定該位填寫的數字。在填充過程中注意判斷前導零的情況以及最大值邊界的情況。

        為方便理解源程序代碼,下面用四個實例的構造來說明。

        例如,設n=120,m=25。要求的整數的位數最多3位,先構造最高位,設填充1,以1開頭的且小於120的整數有32個(1,10~19,100~120),m=25<32,所以1~120字典序中的第25個整數最高位一定是1,確定了一位后,m=m-1=24;之后確定次高位,以0開頭且小於n的兩位整數有11個(0,00~09,因為最高位已確定為1,非0,所以這里的0不是前導0),m=m-11=13,以1開頭的數字有11個(1,10~19),m=m-11=2,以2開頭的數字有11個(2,20~29),m=2<11,所以,次高位填寫2,填寫后,m=m-1=1;之后,確定最低位,以數字0開頭的1位數只有1個,m<=1,所以最低位確定為0,填寫后m=m-1=0,構造結束。1~120字典序中的第25個整數為120。

         再如,設n=2345,m=1680。以1開頭且小於n的整數有1111個,m=m-1111=569,以2開頭且小於n的整數有457個,其中1位數1個,2位數10個,3位數100個,4位數346個(2,20~29,200~299,2000~2345),m=569-457=112,以3開頭且小於n的整數有111個,m=112-111=1,以4開頭的數字有111個,m=1<111,所以1~2345字典序中的第1680個整數的最高位一定為4,填寫后,m=m-1=0,m等於0,填寫結束。所以結果為4。

        又如,設n=2345,m=1675。先構造最高位,以1開頭且小於n的整數有1111個,m=m-1111=564,以2開頭且小於n的整數有457個,m=564-457=107,以3開頭且小於n的整數有111個,m=107<111,所以1~2345字典序中的第1675個整數最高位一定是3,確定了一位后,m=m-1=106;之后確定次高位,符合要求的以0~8開頭的數各有11個(注意:以3開頭的4位數均大於2345),m=m-9*11=7,以9開頭的數有11個(即39,390~399這11個數),m=7<11,所以1~2345字典序中的第1680個整數的次高位一定為9,填寫后,m=m-1=6;之后以0~4開頭的1位數各1個,m=m-5*1=1,填寫5,填寫后m=m-1=0,m等於0,填寫結束。所以結果為395。

         又如,設n=2345,m=1564。先構造最高位,以1開頭且小於n的整數有1111個,m=1564-1111=453,以2開頭且小於n的整數有457個,m=453<457,所以1~2345字典序中的第1564個整數最高位一定是2,確定了一位后,m=m-1=452;之后確定次高位,以0,1,2開頭且小於n的3位以內整數各有111個,m=m-3*111=119,以3開頭的3位以內整數有57個(3,30~39,300~345),m=m-57=62,之后符合要求的以4~8開頭的數各有11個,m=m-5*11=7,以9開頭的數有11個,m=7<11,所以1~2345字典序中的第1564個整數的次高位一定為9,填寫后,m=m-1=6;之后以0~4開頭的1位數各1個,m=m-5*1=1,填寫5,填寫后m=m-1=0,m等於0,填寫結束。所以結果為295。

        (2)源程序。

#include <stdio.h>

typedef long long LL;

int main()

{

    LL n, m;

    LL a[19]={1},b[19]={0};

    int i,j;

    for (i=1; i<=18; i++)

    {

        a[i] = a[i-1]*10;

        b[i] = a[i]+b[i-1]; 

    }

    while (scanf("%lld%lld",&n,&m)!=EOF)

    {

        LL tmp=n;

        int len=0;

        while (tmp)

        {

           len++;

           tmp/=10;

        }

        LL ans=0;

        for (i=0; i<len; i++)   // 從高位開始構造

        {

           int res;

           for (j=((i==0)?1:0); j<=9; j++) // 從小的數開始構造

           {

               tmp = (ans*10+j)<=n;

               if (len-i-2 > 0) tmp += b[len-i-2];  // 長度小於len的數有幾個

               if (len-i-1 > 0)              // 長度等於len的數有幾個

               {

                  if (ans*10+j < n/a[len-i-1])

                      tmp += a[len-i-1];

                  else if (ans*10+j == n/a[len-i-1])

                      tmp += n%a[len-i-1]+1;

               }

               if (m>tmp) m-= tmp;

               else

               {

                  res = j;

                  break;

               }

           }

           ans = ans*10+res;

           m--;                 // 每選了一個數就減一

           if (m==0)  break;

        }

        printf("%lld\n",ans);

    }

    return 0;

}

52-3  有趣的數

問題描述

將1到N的正整數集合中的元素按照字典序排列,例如當N=11時,其順序應該為:1,10,11,2,3,4,5,6,7,8,9。

定義K在N個數中的位置為Q(N,K),例如Q(11,2)=4。現在給出整數K和M,要求找到最小的N,使得Q(N,K)=M。

輸入

輸入包括多行,每行是兩個整數K和M。

輸出

每組輸入輸出一行,該行是最小的N,如果不存在這樣的N就輸出0。

輸入樣例

2 4

100000001 1000000000

輸出樣例

11

100000000888888879

        (1)編程思路。

        先確定整數K的最小位置。

        例如,k=120時,排在它前面的1位數有1個(1),2位數有3個(10、11和12),3位數有20個(100~119),因此120的最小位置pos=1+3+20+1=25。

        再例如K=1680,排在它前面的1位數有1個(1),2位數有7個(10~16),3位數有20個(100~168),4位數有20個(1000~1680),因此1680的最小位置pos=1+7+69+681=758。

        求出K的最小位置pos后,將pos和 M 進行比較。

        若 pos=M,那么只要出現了K 就可以使得K的位置是M,因此N的最小值就是K。

        若 pos>M,無解,因為pos是K的最小位置。不存在其他的位置比pos還要小。

        若 pos<M,說明還有字典序排在K前面的數,但是位數小於等於K的已經窮舉完了,因此肯定有位數大於K的數排在K的前面。逐步增加位數,同時更新最小位數。當某個時刻 pos≥M ,先將pos減回去,算出M 與pos之間的差,再加上 10^x(x是當前位數),最后減去 1(因為從 0 開始算),就是 N的最小值。

       (2)源程序。

#include <stdio.h>

int main()

{

    long long  m,k;

    while (scanf("%lld%lld",&k,&m)!=EOF)

    {

        long long pos=0;

        long long p=1;

        while (k>=p) p*=10;

        p/=10;

        long long temp1=k;

        long long temp2=p;

        while (temp1>0)

        {

            pos+=temp1-temp2+1;

            temp1/=10;

            temp2/=10;

        }

        if (pos>m) printf("0\n");

        else if (pos==m) printf("%lld\n", k);

        else

        {

            long long temp=k*10;

            p*=10;

            if (temp-p==0)

            {

                printf("0\n");

                continue;

            }

            while (pos-p+temp<m)

            {

                pos=pos-p+temp;

                temp*=10;

                p*=10;

            }

            p+=m-pos-1;

            printf("%lld\n", p);

        }

    }

       return 0;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM