我试图解决http://poj.org/problem?id=1426(2002年达卡地区)。虽然我无法提出所需的确切算法,但是当n从1变化到200时,我通过生成二进制数并检查可分性来预先计算所有值。现在我有一个恒定的时间算法:)但我相信这不是解决问题的正确方法。我不想使用图搜索算法,因为这个问题是在一个站点的基本数学下进行的,所以我认为必须有一个数学解决方案来解决这个问题而不给TLE。
答案 0 :(得分:2)
这是剩下的一个简单伎俩。
必须只用0和1来写每个倍数,意味着你想要一个10的一些幂之和的倍数,这意味着某些{a(i)的x = \ sum {10 ^ a(i)} }。要找到我们想要保留的正确索引,您必须记住,作为数字n的倍数意味着x mon n = 0。
所以,所有关于写入10 mod n的幂,并找到一个和为0 mod n的子集。 让我们试试19:
Num -> Num mod 19
1 -> 1
10 -> 10
10^2 -> 5
10^3 -> 12
10^4 -> 6
10^5 -> 3
现在,我们可以看到10 ^ 1 + 10 ^ 4 + 10 ^ 5 = 19即0 mod 19,所以我们的解决方案是110010。 要找到提醒模型19,您不必计算每一个功率,您只需将前一个值mod 19乘以10,然后计算模块。
例如,10 ^ 4 mod 10 = 10 ^ 3 * 10 mod 19 = 12 * 10 mod 19 = 6,这比计算10 ^ 4更容易(也许它不适用于小功率,但想象一下必须计算在制作它之前100 ^ 100 19)。
修改强>
剩下的唯一问题是找到一个总和为0 mod n的子集,假设存在这样的子集。
修改强>
好的,我有一个想法,可以达到n = 200并解决线性时间问题。基本上,你利用了sumn modn迟早会重叠的事实。这是真的,因为hte pigeot原则,但在特定情况下,只有100个整数,让它工作只是一个单纯的情况。无论如何,给定如前所示计算的提醒列表,您开始计算部分总和。如果你遇到了你已经拥有的价值,你就有了解决方案(i-1 1后跟j 0)。如果你遇到提醒0,你也做得很好。
这是我编写的C#代码来测试它:
for (int n = 2; n <= 200; n++)
{
int[] reminder = new int[100];
reminder[0] = 1;
for (int i = 1; i < 100; i++)
{
reminder[i] = (10 * reminder[i - 1]) % n;
}
var lst = reminder.Select((x, y) => new TenPower { Reminder = x, Pow = y })
.ToList();
bool cont = true;
for (int i = 1; (i < 100)&&cont; i++)
{
if (lst[i].Reminder == 0)
{
cont = false;
Console.WriteLine(n +" :: " + Math.Pow(10, lst[i].Pow));
}
else
{
lst[i].Reminder = (lst[i].Reminder + lst[i - 1].Reminder) % n;
if (lst[i].Reminder == 0)
{
cont = false;
Console.WriteLine(n + " :: " + Math.Pow(10, lst[i].Pow));
}
for (int j = i - 1; (j > 0) && cont; j--)
{
if (lst[i].Reminder == lst[j].Reminder)
{
cont = false;
Console.Write(n + " :: ");
for (int k = 0; k < i - j; k++)
{
Console.Write("1");
}
for (int k = i - j-1; k < i; k++)
{
Console.Write("0");
}
Console.WriteLine();
}
}
}
}
}