nyist oj 1172 unlucky number

unlucky number

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 0
描述
我们定义在区间【l,r】之间只出现1和7组合的数是unlucky number,例如:1 、7、 11、17 都是unlucky numbers ,而 13 、27则不是,问在区间【l,r】内有多少 unlucky numbers??
输入
有多组测试数据(不超过100组)
每组输入两个整数l,r( 0 =< l<= r <= 10^18)
输出
每行输入一个结果
样例输入
1 7
样例输出
2

假设有两个数组a[n]和b[n],a[i]表示i位数中共有多少种1和7的组合,我们看看怎么求a[i],首先看第一位,有两种选择(1和7),再看第二位,有两种选择,依次进行,每一位都是两种选择,根据分步乘法计数原理,a[i]=2^i。

b[i]表示1~10^i之间共有多少种1和7的组合,显然b[i]=a[1]+a[2]+a[3]+……a[i]=2^(i+1)-2,即b[i]=(b[i-1]+2)*2-2。

如果计算l~r之间的1和7的组合数,假设某函数f(n)可以计算1~n之间的组合数,则结果等于f(r)-f(l-1),但是如果l是一个字符串的大数的话,还要计算大数减法,为了避免这种情况,可以计算f(r)-f(l),然后如果l是1和7的组合的话,再加上1就行了,简单了许多。

那么如何计算任意一个数的符合条件的数的数目,比如8817,首先在1~1000之内有b[3]个数,然后当千位分别是1和7时,都有a[3]个数,所以是2*a[3],所以总共的情况有b[3]+2*a[3]=30,但是只有这么简单吗,是的,在这个情况下,他只需要O(1)的复杂度,但对于最坏的情况,比如7720,首先在1~1000之内有b[3]个数,然后当千位分别是1时,有a[3]个数,所以是a[3],当千位是7时,他的具体数目要往后面看,看百位是7,当是1时,有了a[2]种可能,当成为7时,还要往后看,十位是2,此时当十位是1时,包含了所有情况,即只有a[1],即所有的可能为b[3]+a[3]+a[2]+a[1]=28。基于此思想,可以形成最坏O(N)复杂度的算法,其中n为字符串的长度,所以此计数不只是局限于long long 内,以下是代码,仅供参考

 
#include <cstdio>
#include <cstring>
int dp[24];
int cau(char *s){
    int len=strlen(s),sum,num,flag;
    sum=dp[len-1];flag=1;
    for(int i=0;i<len-1;i++){
        num=s[i]-'0';
        if(num==0)  break;
        if(num==1)  {if(i==len-2)   flag=0;continue;}
        if(num<7)  {sum+=dp[len-1-i]-dp[len-2-i];break;}
        if(num==7)   {sum+=dp[len-1-i]-dp[len-2-i];if(i==len-2) flag=0;continue;}
        sum+=dp[len-1-i]-dp[len-2-i]+dp[len-1-i]-dp[len-2-i];break;
    }
    if(flag&&len>1)   return sum;
    num=s[len-1]-'0';
    if(num<1)   return sum;
    if(num<7)   return sum+1;
                return sum+2;
}
char h1[24],h2[24];
int main(){
    dp[1]=2;dp[2]=6;
    for(int i=3;i<=24;i++) dp[i]=(dp[i-1]+2)*2-2;
    while(scanf("%s%s",h1,h2)!=EOF){
        int j=1,len=strlen(h1);
        for(int i=0;i<len;i++){
            if(h1[i]=='1'||h1[i]=='7')  continue;
            j=0;break;
        }
        printf("%d\n",cau(h2)-cau(h1)+j);
    }
    return 0;
}
        

以上代码中,dp数组就是代表b数组,h2和h1字符串分别表示r和l这两个数。cau()就是以上说的f()函数。

这个代码还可以再写短一点,让它更美观,不过这不是问题的主要矛盾,还有一个问题,这个cau函数真的能计算任意长度的数字吗,其实,最好的情况下,在计算cau(n)时,当n=10^31时,就会有2^32-2种可能,超出了int的范围,就算把cau()的返回值设为long long,那么n=10^63时,会超出范围,所以这个代码是不完美的,但可以修正,只要把cau()函数的返回值改为字符串,其中的加法改为大数加法就行了,就可以支持任意有限位的整数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值