315 Digital Root Clocks
这个题目比较长,下面简单描述一下。
有一个数字表,显示数字的方式可以看原题,其实是老式计算器那个样子,也像小时候做的奥数题,拿火柴棍摆正正方方的数字。
给它一个输入,比如说 137,首先它会显示 137,然后计算 137 各个数字之和,得到 11,然后显式 11,再计算得到 2,显示 2。
显示数字或者是不显示,都需要一次转换,比如显示 4,要 4 次转换,比如显示 8 然后什么都不显示,需要 7 次转换。
Sam 的表显示的方式很直接:
比如输入 137,显示 137,再关掉,一共需要 次转换,137 各个数字之和是 11,显示 11 再关掉, 次转换,最后的数字 2,需要 次转换。一共需要 40 次转换。
Max 的表稍微智能一点:
比如137变成11的时候,3和1是有公共部分的,7和1也是有公共部分的,那么在关掉137的时候,对应下个数字的地方就不关掉了,显示下一个数字的时候就不需要再打开,用此方法较少转换的次数。
打开137,2 + 5 + 4 = 11次转换,关掉的时候,11的那4个地方不关闭,那么关闭需要7次转换;显示11不需要打开了,关闭11显示2,第二个1的右上角那一个也不要关闭,所以关闭11需要3次转换,打开2,只需要4次转换而不是5次,关闭2需要5次转换。总共需要30次转换。
相比Sam的少了10次。
题目的输入是 到 之间的所有质数,求省下来的转换次数。
其实这个题目看似很难,如果找对了关键点,其实很简单。
所谓省下来的次数,就是 2 个数字直接重叠的那一部分,比如 0 和 8 重叠就是整个 0,6 条边,2 和 4 就重叠了两条边。
于是乎,一个二维数组表示任意两个数字之间的重叠个数。
private static int[,] Overlap = new int[10, 10]
{
{ 6,2,4,4,3,4,5,4,6,5},
{ 2,2,1,2,2,1,1,2,2,2},
{ 4,1,5,4,2,3,4,2,5,4},
{ 4,2,4,5,3,4,4,3,5,5},
{ 3,2,2,3,4,3,3,3,4,4},
{ 4,1,3,4,3,5,5,3,5,5},
{ 5,1,4,4,3,5,6,3,6,5},
{ 4,2,2,3,3,3,3,4,4,4},
{ 6,2,5,5,4,5,6,4,7,6},
{ 5,2,4,5,4,5,5,4,6,6}
};
万一人为计算的时候搞错了呢?我又搞了个函数检查一下,我把数字的边从上到下,从左到右编号。比如数字 1 的两条边就是 3,6,数字 7 的边就是 1,2,3,6。然后计算数字的重叠部分,看看和我手写的矩阵是否一致。第一遍运算的时候,还真发现矩阵里面的一个错误。
private static void Check()
{
var digital = new List<List<int>>();
digital.Add(new List<int>() { 1, 2, 3, 5, 6, 7 });
digital.Add(new List<int>() { 3, 6 });
digital.Add(new List<int>() { 1, 3, 4, 5, 7 });
digital.Add(new List<int>() { 1, 3, 4, 6, 7 });
digital.Add(new List<int>() { 2, 3, 4, 6 });
digital.Add(new List<int>() { 1, 2, 4, 6, 7 });
digital.Add(new List<int>() { 1, 2, 4, 5, 6, 7 });
digital.Add(new List<int>() { 1, 2, 3, 6 });
digital.Add(new List<int>() { 1, 2, 3, 4, 5, 6, 7 });
digital.Add(new List<int>() { 1, 2, 3, 4, 6, 7 });
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
int overlap = digital[i].Intersect(digital[j]).Count();
if (overlap != Overlap[i, j])
{
Console.WriteLine(i + "\t" + j + "\t" + overlap);
}
}
}
}
接着往下想,给两个数字,比如 137 和 11,如何得到重叠部分呢?很简单,从最后一位开始比就可以了,最后一位的两个数字,去查矩阵,很容易得到重叠的值,然后再考察倒数第二位等等。代码如下:
private static int GetCount(int num1, int num2)
{
int count = 0;
while (num1 > 0 && num2 > 0)
{
count += Overlap[num1 % 10, num2 % 10];
num1 /= 10;
num2 /= 10;
}
return count;
}
能够比较两个数字了,那么给定要给输入,一个几百万大的质数,求各个数字之后,对比两个数字之间的重叠值,然后再计算数字之和的数字之和,再对比重叠值,循环到个位数即可。
private static int GetCount(int p)
{
int count = 0;
while (p >= 10)
{
int sum = p.ToString().Select(c => c-'0').Sum();
count += GetCount(p, sum);
p = sum;
}
return count * 2;
}
万事俱备,最后,拿到 到 之间的所有质数,遍历求和即可。