【数论】二段数
#include <iostream>
using namespace std;
// 思路一: 判断n的倍数是不是二段数。
// 已知二段数由两种数字组成,利用枚举。我们把该数每一位的数字用桶排序排到数组里,如果数组的和为2,那么该数为二段数,进行输出即可。 如果不是,进行翻倍后再次判断
// 缺点:运行时间太长
// 思路二
const int maxn = 10010; // 表示最大的位数长度,定义为const,不可变的固定大小
int a[maxn], b[maxn];
// m, s, n = total - m 可得,不用假设。 t
int m, total, s, t, aptotal, apm, aps, apt, k; // 先声明要用到的变量,记录符合要求的最小二段数
int n; // n为输入的数,求既是二段数,又是n的倍数
bool ck() // 返回为bool类型的函数,检测二段数是否大于n
{
int p, r;
if (total > 5) // 剪枝 题目已知n的最大值是99999,是5位数,total大于5的话,该二段数必定大于n。直接返回true即可
return true;
p = s; // 4
r = t; // 1
//m = 0时,s只有一位,因此 m 的初值不能和 total - m一样为 1
for (int q = 0; q < m; q++)
{
p = p * 10 + s; // 444444
}
for (int q = 0; q < total - m; q++)
p = p * 10; // 44444400
for (int q = 1; q < total - m; q++)
r = r * 10 + t; // 11
return p + r > n; // p + r即为二段数本身的数值大小
}
int main()
{
while (cin >> n, n) //?逗号表达式? 输入0退出循环
{
printf("%d: ", n);
if (n == 1)
{ // 剪枝
puts("10");
continue;
}
a[0] = 1;
b[0] = 1;
// 数列1:a[1]=1,a[i+1]=(a[i]*10+1) mod N,i = 1,2,3,… a[i]就是连续i个1除以N的余数。
// 数列2:b[0]=1,b[i+1]=b[i]*10 mod N,i = 0,1,2,3,… b[i]就是10的i次方除以N的余数。
for (int i = 1; i < 9999; i++) // 初始化a[i]、b[i]两个数组
a[i] = (a[i - 1] * 10 + 1) % n; // a[1] = 11 % n a[2] = a[1] % n ...最后存入数组的是一串重复循环的数字。当n=30时,数组内容是1, 11, 21循环
for (int i = 1; i < 999; i++)
b[i] = b[i - 1] * 10 % n; // b[1] = 10 % n b[2] = 100 % n
// 接下来只要枚举m,s,n,t就可以了。按照(m+n)的值从小到大枚举,(m+n)确定后枚举m,则n可以直接计算出来,不需要枚举。m和n确定之后枚举s和t。一旦找到解,后面的(m+n)值就不需要继续枚举下去了。
for (total = 1, aps = 0; total < 9999; total++)
{
k = 0;
if ((n % 10 == 0 || n % 25 == 0) && total > 11) // 剪枝 当total == 12时,仍然未求出 此时,m从 2位开始 n 为11,即1100000000000
{
k = total - 11;
}
for (m = k; m < total; m++)
{
for (s = 1; s < 10; s++)
{
for (t = 0; t < (n % 10 ? 10 : 1); t++) // 剪枝:n是10的倍数,t只能取0
{
// t != s 确保是二段数
// a[m] * b [n] * s + a[n] * t即为此时的二段数 判断是否整除n,整除n就是n的倍数
// ck()判断二段数是否大于n
//!aps || s < aps !aps确保第一次满足条件的二段数保存一次,s<aps确保 在total不变的情况下,二段数不会随便被更新.
// 只有当s更小时,即有另一个比当前保存的二段数更小时,才会更新 因为循环先加的是 total-n 的值。 例如 n = 52时, 二段数先是988,之后m+1后,更新为884
if (t != s && (((long long)a[m]) * b[total - m] * s + a[total - m - 1] * t) % n == 0 && ck() && (!aps || s < aps))
{
aptotal = total;
apm = m;
aps = s;
apt = t;
}
}
}
}
if (aps)
break;
}
for (int x = 0; x < apm + 1; x++) // 打印输出二段数
{
cout << aps;
}
for (int x = 0; x < aptotal - apm; x++)
{
cout << apt;
}
cout << endl;
}
}