查找缺失的排列

时间:2019-05-29 09:49:11

标签: algorithm heuristics

这个问题由Google在最近的一轮codejam回合中提出,它仍然困扰着我如何设法解决它。请告诉我该怎么做:

  

去,去,电源安排器!每个人都喜欢这支由五个超级英雄组成的团队   戴着字母A,B,C,D和E的高中生   并排面对邪恶的怪物,他们安排他们的团队   以120种可能的左右顺序之一进行分配   各种不同的战术超级大国。他们更受欢迎   比忍者神龟!

     

该节目的一些评论家声称该团队只有安排   头,使演出的所有人可以卖出120套各5套   行动人物,每个人物都以不同的团队为特色   从左到右的顺序,粘贴到基础上,以便不能   重新排列。作为狂热的Power Arrangers粉丝,您已经收集了119个   这些集合,但是您不记得丢失了哪些集合。您的   沿着架子水平排列119套   从左到右顺序总共有119×5 = 595个可动人偶。你做   不记得这些安排是如何安排的,但是您知道   集合的置换是从所有样本中随机随机选择的   可能的排列,并且对于每种情况都是独立的。

     

您不想浪费任何时间弄清楚自己是哪一套   缺少,因此您打算查看最多F个数字上的字母   架子。例如,您可以选择查看广告上的字母   左边的第八位数字,这将是左边的第三位数字   从左数第二个数中左移。看人物的时候   只从那一个数字得到信件;字母很难看   和其他团队成员看起来非常相似!

     

最多检查F个数字后,您必须找出哪个   缺少套,因此您可以完成收藏并准备好   面对任何可能的邪恶威胁!

     

输入和输出

     

这是一个互动问题。您应该确保已阅读   常见问题解答中“互动问题”部分中的信息。

     

最初,您的程序应读取包含两行的一行   整数T(测试用例数)和F(数字)   允许每个测试用例进行检查。然后,您需要进行T检验   案例。

     

在每个测试用例中,统一选择丢失的一组图形   从所有可能的集合中随机抽取,其余集合的顺序   同样从所有可能的顺序中随机选择。每一个   选择是独立于所有其他选择和您的输入的。

     

在每个测试用例中,您的程序将最多处理F +1次交换   与我们的法官。您可以组成以下形式的F个交易所:

     
      
  • 您的程序输出一行,其中包含1到595之间(包括1和595)的单个整数,指示哪个数字(从左到右的顺序)   沿着架子)。再举一个例子,589   代表第二组中从左数的第四个数字   是的。
  •   
  • 法官用一行包含一个大写字母A,B,C,D或E的行作为回应,指出该图上的字母。如果你   发送了无效数据(例如,数字超出范围或行格式错误),   法官将改为以单行回应   大写字母N。
  •   
     

然后,在完成与您一样多的上述F交换之后   如果需要,您必须再次交换以下格式:

     
      
  • 您的程序输出一行包含五个大写字母的单个字符串:对应于缺失集合的排列   (例如CADBE)。
  •   
  • 法官的回答是一行包含一个大写字母:如果回答正确,则为Y;否则,为N(或   提供了格式错误的行)。如果收到Y,则应开始   下一个测试用例,如果没有更多测试用例,则停止发送输入。
  •   
     

法官将N发送到您的输入流后(因为   无效的数据或错误的答案),它将不会发送其他任何信息   输出。如果您的程序继续等待之后的判断   收到N,您的程序将超时,从而导致时间限制   超出错误。请注意,您有责任   程序及时退出以收到错误答案的判断,而不是   超过时间限制错误。与往常一样,如果超出了内存限制,   或程序出现运行时错误,您将收到相应的   判断。限制

     

1≤T≤50。时间限制:每个测试装置40秒。内存限制:1GB。   选择缺少的集以及其余集的顺序   均匀且独立地随机。测试集1(可见)

     

F =475。测试仪2(隐藏)

     

F = 150。

1 个答案:

答案 0 :(得分:4)

您可以尝试这样的事情:

  • 使用前119个查询来查询每个“块”的第一个元素,即第0、5、10等元素
  • 计算找到每个字母的频率,以及在哪里;每个起始字母应该有24个排列,但是对于一个,只有23个
  • 继续这23个排列并查询这些块的第二个字母,例如如果起始位置为15、40、60,...,则查询第16、41、61,...字母
  • 再次计算一下找到第二个字母的频率;每个字母应该有6个,但是一个字母只有5个
  • 继续输入第5个字母并查询第三个字母;对于其中之一,只有一个排列,而不是两个
  • 现在您知道丢失的排列的前三个字母;最后查询单个排列的第四个(或第五个)字母,该字母与最后一步的前三个数字相同,然后您就可以推断出缺失排列的最后两个数字

这样,您将需要119 + 23 + 5 + 1 = 148个查询才能找到丢失的排列。

Python中的示例实现:

import itertools, random, collections
permutations = list(itertools.permutations("ABCDE"))
random.shuffle(permutations)
permutations.pop()
flat_permutations = [c for p in permutations for c in p]

queries = 0
candidates = [i * 5 for i in range(119)]
missing = ""
for i in [0, 1, 2, 4]:
    where = collections.defaultdict(list)
    for t in candidates:
        queries += 1
        c = flat_permutations[t + i]
        where[c].append(t)
    c, candidates = min(where.items(), key=lambda item: len(item[1]))
    missing += c

# we queried for the 5th and used it as 4th in 'missing'
missing += next(c for c in "ABCDE" if c not in missing)

print("queries", queries)
print("missing", missing)
print("correct?", tuple(missing) not in permutations)

这将总是找到丢失的排列,但也会总是 1)进行148个查询。

如果F较小,则可以只查询随机位置,然后使用概率来猜测最不适合这些猜测的排列。


1)从技术上讲,您可以以更少的查询找到它,例如如果您非常幸运,并且经过100次查询后,您已经拥有4个字母,每个字母有24个职位,那么您不需要再执行最后19个查询,但这纯粹是个好运,而且机会很小。