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的倍数的最小二段数。
思路:
-
按照倍数,将n扩大倍数,判断是否为二段数。结果会溢出。
-
利用数组来储存数字的每一位,定义数组加法,然后每次加n,判断是否为二段数。结果超时。
-
利用余数的性质。
设二段数有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+b−1+10a+b−2+⋯+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×(10b−1+10b−2+⋯+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[i−1]×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本身)。
结果超时。
-
进行剪枝,n的个位为偶数,其二段数的结尾不可能为奇数;n的个位为5,其二段数的结尾一定是0/5。
注意点:
- 在计算时,从和n相同的位数开始计算,但是如果n为二段数的话,第一次遍历计算出的结果是n本身,所以对n进行二段数判断,同时记录下得到二段数结果的次数,当n不为二段数时,第一次为结果;当n为二段数时,第二次为结果。
- 遍历的顺序:需要按照数字从小到大进行遍历,所以最外层是数字位数,往内要先遍历a和b两个数字,再遍历位数,并且后半部分的位数从1开始遍历。
- 前半部分的数字不能为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;
}
}
}
}
}
}
}
}