数列问题

题目详情
给定一个正整数n,我们把从1到n的数连接起来形成一个数n1,例如n=11,我们得到的n1=1234567891011,然后我们删掉这个数中在偶数位置的数字,得到数字n2(例子中n2=1357901), 我们再删除n2中奇数位置的数字得到数字n3(例子中n3=370),重复上面的删数字过程,直到剩余一个数字,求最后剩余的数字。 (1<=n<=99999)。

这道题我想到了两种解法,一种是简单的双循环,我当时觉得15分的题不会这么简单,联想到之前碰到的极限数据,就否定了这个方案,
之后又想了下面的第二个方法,只要O(logn)的复杂度,这道题居然一次过了.然后我手贱把第一个代码也贴上去,又过了,瞬间就迷茫了...

//双循环版
#include<stdio.h>
#include<string.h>

#define N 99999 * 5
char  str[N];
int  mark[N];

int  remain( int  n)
{
     int  i;
     char  *p = str + 1;
    
     int  len;
     int  start;
     int  end;
     int  kill;
     for (i = 1; i <= n; i++)
    {
        sprintf(p,  "%d" , i);
        p += strlen(p);
    }
    len = strlen(str + 1);
    
    start = 1;
    end = len;
    kill = 2;
    
    memset(mark, 0,  sizeof (mark));
     while (start < end)
    {
         int  num = 1;
         int  i;
         for (i = start; i <= end; i++)
        {
             if (len > 1 && mark[i] == 0)
            {
                 if (num  == kill)
                {
                    mark[i] = 1;
                    len--;
                }
                num = num == 2 ? 1 : 2;
            }
        }
         while (mark[start] == 1)
        {
            start++;
        }
         while (mark[end] == 1)
        {
            end--;
        }
        kill = kill == 2 ? 1 : 2;
    }
     return  str[end] -  '0' ;
}
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int  main()
{    
    printf( "%d" ,remain(99999));
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。

//优化版
#include<stdio.h>
#include<string.h>

#define N 99999 * 5

int remain (int n)
{
    int i;
    int len;
    int start,  end;
    int jump,  kill;
    char str[N];
    char *p = str + 1;
    
    for(i = 1; i <= n; i++)
    {
        sprintf(p, "%d", i);
        p += strlen(p);
    }
    len = strlen(str + 1);
    
    start = 1;
    end = len;
    jump = 1;
    kill = 2;
    
    while(len > 2)
    {
        if(len % 2 == 0 && kill == 2)
        {
            end -= jump;
            len /= 2;
        }
        else if(len % 2 == 0 && kill == 1)
        {
            start += jump;
            len /= 2;
        }
        else if(len % 2 == 1 && kill == 1)
        {
            start += jump;
            end -= jump;
            len /= 2;
        }
        else
        {
            len -= len / 2;
        }
        
        kill = kill == 2 ? 1 : 2;
        jump *= 2;
    }
    if(len == 2 && kill == 2)
    {
        return str[start] - '0';
    }
    return str[end] - '0';
}
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{    
    printf("%d",remain(99999));
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值