坏消息,有人


10

在《 Futurama》情节中,剧组的Benda囚犯成员相互交换尸体,但要注意的是,没有一对尸体可以交换思想。

挑战

编写一个程序或函数,该程序或函数接受已经发生的有效的身心交换,并输出一组合法的交换,这些交换将使每个头脑返回其原始身体。这些身心集合的标识符必须是不包含换行符的字符串。您最多可以添加两个(没有明显名字的)人,他们之前没有与输入组的交换。(证明您最多只需要2个其他实体),但是,您必须添加解决问题所需的最少人数。

输入和输出可以采用任何清楚的形式,但是,任何其他信息都不能存储在其中。您可能会认为它始终有效。这是代码高尔夫,所以获胜者是字节最少的提交。

例子

[('A','B'),('C','D')] -> [('A','C'),('B','D'),('A','D'),('B','C')]

['A','B'] -> ['C','D','A','C','B','D','A','D','B','C']

[('A','B'),('C','D'),('A','C'),('A','D')] -> [('B', 'E'), ('A', 'E'), ('C', 'B'), ('C', 'E')]

"A\nB\nC\nD\n" -> "A\nC\nB\nD\nA\nD\nB\nC\n"

节目中的一个:

[("Amy","Hubert"),("Bender","Amy"),("Hubert","Turanga"),("Amy","Wash Bucket"),("Wash Bucket","Nikolai"),("Phillip","John"),("Hermes","Turanga")]

演出的解决方案,如下所示,是无效的:

[("Clyde","Phillip"),("Ethan","John"),("Clyde","John"),("Ethan",Phillip"),("Clyde","Hubert"),("Ethan","Wash Bucket"),("Clyde","Leela"),("Ethan","Nikolai"),("Clyde","Hermes"),("Ethan","Bender"),("Clyde","Amy"),("Ethan","Hubert"),("Clyde","Wash Bucket")]

这是无效的,因为Ery和Clyde是不必要的,因为Fry Phillip,Zoidberg John和Hermes Hermes 很少使用该机器。下面提供了针对这种情况的有效解决方案:

[("Philip","Hubert"),("John","Wash Bucket"),("Philip","Turanga"),("John","Nikolai"),("Philip","Hermes"),("John","Bender"),("Philip","Amy"),("John","Hubert"),("Philip","Wash Bucket")]

请注意,对于任何有效输入,显然都有许多可能的答案。任何有效。


我们可以假设不会使用某些名称吗?
feersum'1

@feersum Nope,这是挑战的一部分;)
FryAmTheEggman

1
@feersum您的意思是是否将整个输入作为字符串?然后是的,但是,您可以假设名称之间没有换行符。(现在进行编辑)
FryAmTheEggman

1
您的节目输入解决方案不起作用。最后,艾米和本德尔被调换。正确的解决方法是[('Nikolai', 'Phillip'), ('Nikolai', 'Hubert'), ('Nikolai', 'Turanga'), ('Nikolai', 'Bender'), ('Phillip', 'Amy'), ('John', 'Wash Bucket'), ('Nikolai', 'John'), ('Phillip', 'Wash Bucket'), ('Hubert', 'John'), ('Bender', 'Hermes')]
Jakube

1
@Jakube对不起,看来我在进入演出情况时打了错字。我相信现在已经解决,解决方案还可以。
FryAmTheEggman 2015年

Answers:


3

Python 3:328个字符(慢),470个字符(快)

一个认真的答案可能太长了。

慢和短代码:

from itertools import*
def d(u,s):i,j=map(u.index,s);u[i],u[j]=u[j],u[i]
def f(Q):
 n=set()
 for s in Q:n|=set(s)
 n=list(n)
 while 1:
  for t in permutations(i for i in combinations(n,2)if not set((i,i[::-1]))&set(Q)):
   u=n[:];j=0
   for s in Q:d(u,s)
   for s in t:
    j+=1;d(u,s)
    if n==u:return t[:j]
  n+=[''.join(n)]

d(u,s)套用su。在main方法中f(Q),我首先n使用set操作生成所有人员的列表,然后将结果转换回列表。该while 1-loop当然不是一个无限循环的。在其中,我也尝试使用我所拥有的人来解决问题n。如果不成功,我通过组合所有姓名来添加另一个人n+=[''.join(n)]。因此,while 1-loop最多执行3次(请参阅问题中的证明)。

解决问题是蛮力的。我生成所有合法的交换并尝试所有排列for t in permutations(i for i in combinations(n,2)if not set((i,i[::-1]))&set(Q))。如果每个人都在自己的身体中,我将返回交换顺序。

用法:

print(f([('A','B'),('C','D')]))
print(f([('A','B')]))
print(f([('A','B'),('C','D'),('A','C'),('A','D')]))

未来世界的例子花了太长时间。有9个人,因此有36个可能的掉期,其中28个是合法的。所以有26个!法律排列。

更快的代码

def w(u,s):i,j=map(u.index,s);u[i],u[j]=u[j],u[i]
def f(Q):
 n=set()
 for s in Q:n|=set(s)
 while 1:
  n=list(n);u=n[:];l=len(n)
  for s in Q:w(u,s)
  for d in range((l*l-l)//2-len(Q)+1):r(n,u,Q,[],d)
  n+=[''.join(n)]
def r(n,u,Q,t,d):
 m=0;v=u[:];l=len(u)
 for i in range(l):
  if n[i]!=v[i]:m+=1;w(v,(n[i],v[i]))
 if m<1:print(t);exit()
 for i in range(l*l):
  s=n[i//l],n[i%l]
  if m<=d and i//l<i%l and not set([s,s[::-1]])&set(Q+t):v=u[:];w(v,s);r(n,v,Q,t+[s],d-1)

该函数f(Q)具有迭代加深方法。它首先尝试深度= 0,然后深度= 1,直到深度=(l * ll)// 2-len(Q),这是合法移动的最大次数。像较慢的代码一样,它随后添加了另一个人,然后重试。

递归函数r(n,u,Q,t,d)尝试u通过d交换来解决当前头寸。n是已解决的位置,Q输入的动作和t已经完成的动作。它首先计算m所需的掉期下限(通过用合法和非法掉期解决状态)。如果m== 0,则所有人都在正确的身体中,因此它将打印解决方案t。否则,它将尝试所有可能的交换s,如果m<d(修剪),d>1(已经包含在中m<di//l<i%l(不允许像('A','A')or ('A','B')和进行交换('B','A'))和not set([s,s[::-1]])&set(Q+t)s尚未执行)。

用法:

f([("Amy","Hubert"),("Bender","Amy"),("Hubert","Turanga"),("Amy","Wash Bucket"),("Wash Bucket","Nikolai"),("Philip","John"),("Hermes","Turanga")])

它在使用pypy的笔记本电脑上大约需要17秒,而在没有pypy的情况下大约需要2分钟,可以找到针对未来派问题的最佳交换。请注意,当使用相同的参数调用时,两种算法都输出不同的解决方案。这是因为python-的哈希算法setn每次以不同的顺序存储人。因此,算法每次运行可能更快或更慢。

编辑:原始的futurama测试用例是错误的,更正后的测试用例的最优解为9而不是10,因此更快。pypy为2秒,没有为7秒。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.