poj1019 递推 number sequence

按这样的方式将字符串分组:1,12,123,...,123...n,... 

用 f【】表示每一组的长度(数字的个数),所以:

   1          ~         9   :   f【i】= f 【i - 1】+ 1;

   10        ~       99     :     f【i】= f 【i - 1】+ 2;

   100       ~       999    :    f【i】= f 【i - 1】+ 3;

    1000     ~    9999    :    f【i】= f 【i - 1】+ 4;

   10000  ~ 99999    :   f【i】= f 【i - 1】+ 5;

同时用sum【】记录前i组总共的数字个数,递推关系为sum【i】= sum【i-1】+ f【i】

#include <iostream>
#include <cmath>
using namespace std;

#define MAXN 31268
unsigned int f[MAXN+1], sum[MAXN+1];

void init()
{
    int i;

    sum[0] = f[0] = 0;
    for (i = 1; i <= 9; i++) { f[i] = f[i-1] + 1; sum[i] = sum[i-1] + f[i]; }
    for (i = 10; i <= 99; i++) { f[i] = f[i-1] + 2; sum[i] = sum[i-1] + f[i]; }
    for (i = 100; i <= 999; i++) { f[i] = f[i-1] + 3; sum[i] = sum[i-1] + f[i]; }
    for (i = 1000; i <= 9999; i++) { f[i] = f[i-1] + 4; sum[i] = sum[i-1] + f[i]; }
    for (i = 10000; i <= MAXN; i++) { f[i] = f[i-1] + 5; sum[i] = sum[i-1] + f[i]; }
    /*
      以上5个for循环可由以下代码代替,写成上面的形式更便于理解。
      for (i = 1; i <= MAXN; i++) {
          f[i] = f[i-1] + (int)log10(i+0.0) + 1;
          sum[i] = sum[i-1] + f[i];
      }
    */
}

int BSearch(int l, int r, int tar)
{
    int m;

    while (l < r) {
        m = (l + r) >> 1;
        if (sum[m] < tar) l = m + 1;
        else r = m;
    }
    return l;
}

int getDigit(int m, int nth) {
    int pos = nth - sum[m-1], len = 0, i;
    //pos为所求数在第m组中的位置

   //从1开始用len记录长度,当长度大于pos后跳出,注意此处其实加到i-1的时候pos>len就不成立了

    for (i = 1; pos > len; i++)
         len += (int)log10(i+0.0) + 1;
// 所求数实际是整数i-1的第len-pos+1位,return后的操作就是取出这一位数
  return (i-1)/(int)pow(10.0, len-pos+0.0) % 10;
}

int main()
{
    int t, n;
    init(); 
    cin >> t; 
    while (t--) { 
       cin >> n; 
       int m = BSearch(1, MAXN, n); //位置n属于第m个分组 
       cout << getDigit(m, n) << endl;
    }
    return 0;
}












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值