牛客 BC120 小乐乐与二段数

BC120 小乐乐与二段数

题目描述

小乐乐从老师口中听到了二段数这个名词,想更深入的了解二段数。

二段数是这样的正整数:恰好包含两种不同的十进制数字s和t,s不是0,并且s的所有出现均排列在所有的t的前面。例如,44444411是二段数(s是4,t是1),41、10000000和5555556也是。但4444114和44444都不是二段数。

这时老师问小乐乐:给你一个任意的正整数n,你能求出比n大并且是n的倍数的最小二段数吗?请你帮助小乐乐解答这个问题。

输入描述:

多组输入,每组输入包含一个正整数n (1 ≤ n ≤ 99999)

题目保证测试数据总数不超过500组,当输入n=0时程序结束。

输出描述:

对于每组测试用例,输出正整数n,后面紧跟“: ”,输出答案并换行,即比n大且是n的倍数的最小二段数。

思路:

  1. 按照倍数,将n扩大倍数,判断是否为二段数。结果会溢出。

  2. 利用数组来储存数字的每一位,定义数组加法,然后每次加n,判断是否为二段数。结果超时。

  3. 利用余数的性质。

    设二段数有a个c,b个d组成

    将数分成前a位和后b位进行计算:

    c × ( 1 0 a + b − 1 + 1 0 a + b − 2 + ⋯ + 1 0 b )   m o d   n c\times(10^{a+b-1}+10^{a+b-2}+\cdots+10^{b})\ mod\ n c×(10a+b1+10a+b2++10b) mod n

    d × ( 1 0 b − 1 + 1 0 b − 2 + ⋯ + 1 0 1 + 1 0 0 )   m o d   n d\times(10^{b-1}+10^{b-2}+\cdots+10^1+10^0)\ mod\ n d×(10b1+10b2++101+100) mod n

    利用一个数组sum储存: s u m [ i ] = ( s u m [ i − 1 ] × 10 + 1 )   m o d   n sum[i]=(sum[i-1]\times10+1)\ mod \ n sum[i]=(sum[i1]×10+1) mod n

    所以两部分分别为

    c × ( s u m [ a + b ] − s u m [ b ] )   m o d   n c\times(sum[a+b]-sum[b])\ mod\ n c×(sum[a+b]sum[b]) mod n

    d × s u m [ b ]   m o d   n d\times sum[b]\ mod\ n d×sum[b] mod n

    遍历总位数,c,d,a(b),将两部分求和,如果和为n的倍数,则为题目所求(需要判断不是n本身)。

    结果超时。

  4. 进行剪枝,n的个位为偶数,其二段数的结尾不可能为奇数;n的个位为5,其二段数的结尾一定是0/5。

注意点:

  1. 在计算时,从和n相同的位数开始计算,但是如果n为二段数的话,第一次遍历计算出的结果是n本身,所以对n进行二段数判断,同时记录下得到二段数结果的次数,当n不为二段数时,第一次为结果;当n为二段数时,第二次为结果。
  2. 遍历的顺序:需要按照数字从小到大进行遍历,所以最外层是数字位数,往内要先遍历a和b两个数字,再遍历位数,并且后半部分的位数从1开始遍历。
  3. 前半部分的数字不能为1,两部分的数字不能相同
#include<iostream>
#define N 10005
using namespace std;
bool iserduanshu(int n) //判断是否为二段数,避免计算得到其本身
{
    int a,b;
    a = n%10;
    if(n<10)
        return false;
    while(n!=0)
    {
        if(n%10!=a)
        {
            b=n%10;
            break;
        }
        n/=10;
    }
    if(n==0)
        return false;
    while(n!=0)
    {
        if(n%10!=b)
        {
            return false;
        }
        n/=10;
    }
    return false;
}
int main()
{
    int n;
    while(cin>>n,n)
    {
        int erduanshu=iserduanshu(n);
        int a,b,c,d; // a个c b个d
        int times=0; // 计算出结果的次数
        int total; // n的位数
        int temp=n;
        for(total=0;temp>0;total++)
        {
            temp/=10;        
        } // 计算出n的位数
        int sum[N];
        sum[1]=1;
        for(int i=2;i<N;i++)
        {
            sum[i] = (sum[i-1]*10+1)%n;
        }
        for(int i=total;;i++) //注意遍历嵌套的顺序
        {
            if(times-erduanshu==1)
                break;
            for(c=1;c<10;c++)
            {
                if(times-erduanshu==1)
                	break;
                for(d=0;d<10;d++)
                {
                    if(times-erduanshu==1)
               	 		break;
                    if(c==d) // 两个数字不能相同
                        continue;
                    if(n%2==0&&d%2==1) // 剪枝
                        continue;
                    if(n%5==0&&d%5!=0) // 剪枝
                        continue;
                    for(b=1;b<i;b++)
                    {
                        a = i - b;
                        int res = (sum[b]*d+ (sum[i]-sum[b])*c)%n;
                        if(res==0)
                        {
                            times++;
                            if(times-erduanshu==1)
                            {
                                cout<<n<<':'<<' ';
                                for(int j=0;j<a;j++)
                                    cout<<c;
                                for(int j=0;j<b;j++)
                                    cout<<d;
                                cout<<endl;
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值