建立有毒的葡萄酒测试计划程序


16

最近在Puzzling.SE上,我遇到一个问题,即当毒药仅在两种成分都被喝下时才激活时,确定更大数量的哪两个瓶子是有毒的。最终,它变得相当折磨,大多数人使用完全不同的算法设法将其减少到18或19个囚犯。

原始问题说明如下:

您是喜欢举行派对的中世纪王国的统治者。这位上次试图毒害您的葡萄酒瓶的朝臣大怒,得知您设法从十名囚犯中识别出他在1,000瓶中毒了。

这次他有点狡猾。他研发了一种复合毒药 P:一种二元液体,只有两种单独无害的成分混合时才致命。这类似于环氧树脂的工作原理。他又给您寄了1,000瓶葡萄酒。一瓶有成分C_a,另一瓶有成分C_b。(P = C_a + C_b

喝完这两种成分的任何人都将在喝完最后一种成分的夜晚的午夜中途死亡,无论他们在白天何时吸收液体。每种有毒成分都保留在体内,直到第二种成分激活为止,因此,如果一天喝一种成分,第二天喝另一种成分,则第二天结束时您将在午夜死亡。

您下一场聚会还有两天。您需要使用最少多少名囚犯进行测试才能识别出哪两个瓶子受污染,以及在该数目的囚犯身上需要遵循什么算法?


额外的
好处是,假设您有20名囚犯的固定限制,那么理论上可以测试的最大瓶子数量是多少,从而得出关于哪些瓶子受到影响的准确结论?

您的任务是建立一个程序来解决奖金问题。对于n囚犯,您的程序将制定一个测试计划,以便能够mm尽可能大的瓶子中检测出两个有毒的瓶子。

您的程序最初将N囚犯人数作为输入。然后将输出:

  • M,您将尝试测试的瓶子数量。这些瓶子将被标记为从1M

  • N 行,其中包含每个囚犯将喝的瓶子的标签。

然后,您的程序将输入第一天死亡的囚犯作为输入,第一行的囚犯为1,下一行的囚犯为2,等等。然后,它将输出:

  • N更多的行,其中包含每个囚犯将喝的瓶子的标签。死囚将有空白行。

然后,您的程序将输入第二天哪些囚犯死亡的信息,并输出两个数字AB,表示您的程序认为哪个瓶子包含毒药。

两个囚犯和四瓶一例输入可能会去这样,如果瓶子13中毒:

> 2      // INPUT: 2 prisoners
4        // OUTPUT: 4 bottles
1 2 3    // OUTPUT: prisoner 1 will drink 1, 2, 3
1 4      // OUTPUT: prisoner 2 will drink 1, 4
> 1      // INPUT: only the first prisoner died
         // OUTPUT: prisoner 1 is dead, he can't drink any more bottles
3        // OUTPUT: prisoner 2 drinks bottle 3
> 2      // INPUT: prisoner 2 died
1 3      // OUTPUT: therefore, the poisoned bottles are 1 and 3.

The above algorithm may not actually work in all
cases; it's just an example of input and output.

您的程序的测试计划表必须成功确定每对可能的中毒瓶,才能使其有效提交。

您的程序将按照以下标准得分:

  • 它可以识别出这种情况下的最大瓶子数N = 20

  • 箱子的瓶数,N = 21此后依次增加箱子数。

  • 代码的长度。(短代码胜出。)


如果一天内有多名囚犯死亡,输入将如何显示?您的示例都没有涉及这种情况,因此规范对我来说是模棱两可的。
ESultanik

这是一条单行的死囚名单吗?
ESultanik

短代码比瓶子的数量重要吗?像我在最近的编辑中所做的那样,增加代码的长度以使其再处理一个瓶子是否有效果?
pppery

瓶数优先。如果您使代码更长,更复杂以挤入更多瓶子,那将很有效率。
JoeZ。2015年

在原始问题中,只有2天时间可以解决问题。这也是挑战的规则吗?(这严重限制了可能的解决方案,但是无限的日子将变得容易)
LukStorms 2015年

Answers:


7

Python 2.7.9 — 21瓶

假设ESultanik的推测正确,即多名囚犯死亡时的投入是什么

r=raw_input;s=str;j=s.join;p=int(r());z=range;q=z(p);x=z(p+1)
print s(p+1)+"\n"+j("\n",(j(" ",(s(a) for a in x if a!=b)) for b in q))
v=r().split();d=[s(a) for a in q if s(a) not in v];d+=[p]if len(d)==1 else [];
print "\n"*p,;r();print j(" ",[s(a) for a in d])

算法:每个囚犯都从每个瓶子中喝酒,除了数量(第一个囚犯不喝第一瓶)。如果他们不死,他们的号码瓶将被毒死。如果只有一名囚犯幸存,多余的瓶子将被毒化。


3

Perl 5、66瓶

(72瓶21名囚犯)

囚犯最好分为两组。瓶子分为几组。

第一组的每个囚犯都将饮用除一套以外的所有饮品。将有1或2个幸存者。他们没有喝的1套或2套将持续到第2天。

在第2天,其余囚犯(包括幸存者)从所有剩余的瓶子中喝酒,除了一个。
当两名囚犯幸存下来时,他们不喝的瓶子就会中毒。
如果只剩下一名囚犯,那么他们都喝的瓶子也是可疑的。

该代码包括有助于测试的额外功能。当将浸泡过的药瓶作为附加参数添加时,它将不会要求输入有关谁死亡的信息。

($p,$f,$l)=@ARGV;
$p=9if!$p;
$m=$p-(2*int($p/4))+1;
$n=$p-$m+2;
$b=$m*(($n+1)/2);
@M=(1..$m);
print"Prisoners: $p\nBottles: $b\n";
# building the sets of items
for$x(@M){
    $j=$k+1;$k+=($n+1)/2;
    $s=join",",($j..$k);
    $A[$x]=$s
}
# assigning the sets to the actors
for$x(@M){
    @T=();
    for$j(@M){if($x!=$j){push@T,split/,/,$A[$j]}}
    print"Prisoner $x drinks @T\n";
    $B[$x]=join",",@T
}
if(!$f||!$l){
    # manual input
    print"Who dies: ";
    $_=<STDIN>;chomp;
    @D=split/ /;
    %h=map{($_,1)}@D;
    @S=grep{!$h{$_}}(@M)
} 
else{
    # calculate who dies based on the parameters
    for$x(@M){
        $d=0;
        map{if($_==$f||$_==$l){$d++}}split/,/,$B[$x];
        if($d>1){push@D,$x}else{push@S,$x}
    }
}
for(@D){print"Prisoner $_ dies\n"}

# calculate the remaining items
for(@S){push@R,split/,/,$A[$_]}@R=sort{$a<=>$b}grep{!$g{$_}++}@R;

# different set of actors if there were 1 or 2 sets remaining
if(@S>1){@S=($S[0],$m+1..$p,$S[1],0)}else{@S=($m+1..$p)};

$i=0;@B=@D=();
# assign an item to each actor
for$x(@S){
    @T=();
    for($j=0;$j<@R;$j++){
        if($i!=$j){push@T,$R[$j]}
    }$i++;
    print"Prisoner $x drinks @T\n"if$x>0;
    $B[$x]=join",",@T
}

if(!$f||!$l){
    # manual input
    print"Who dies: ";
    $_=<STDIN>;chomp;
    @D=sort split/ /;
    if(@D<@S-1){push@D,0} # because the set that noone drinks isn't manually put in
    %h=map{($_,1)}@D;
    @L=grep{!$h{$_}}(@S);
}
else{
    # calculate who dies based on the parameters
    @D=();
    for$x(@S){
        $d=0;
        map{if($_==$f||$_==$l){$d++}}split/,/,$B[$x];
        if($d>1){push@D,$x}else{push@L,$x}
    }
}

for(@D){print"Prisoner $_ dies\n"if$_>0}

# calculate the remaining items
for(@L){push@F,split/,/,$B[$_]}
map{$c{$_}++}@F;
for(keys%c){push(@Z,$_)if$c{$_}==1}
@R=sort{$a<=>$b}@Z;

print"Suspected bottles: @R"

测试

$ perl poisened_bottles.pl 20
Prisoners: 20
Bottles: 66
Prisoner 1 drinks 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 2 drinks 1 2 3 4 5 6 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 3 drinks 1 2 3 4 5 6 7 8 9 10 11 12 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 4 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 5 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 6 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 7 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 8 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 9 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 55 56 57 58 59 60 61 62 63 64 65 66
Prisoner 10 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 61 62 63 64 65 66
Prisoner 11 drinks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
Who dies: 2 3 4 5 6 7 8 9 10
Prisoner 2 dies
Prisoner 3 dies
Prisoner 4 dies
Prisoner 5 dies
Prisoner 6 dies
Prisoner 7 dies
Prisoner 8 dies
Prisoner 9 dies
Prisoner 10 dies
Prisoner 1 drinks 2 3 4 5 6 61 62 63 64 65 66
Prisoner 12 drinks 1 3 4 5 6 61 62 63 64 65 66
Prisoner 13 drinks 1 2 4 5 6 61 62 63 64 65 66
Prisoner 14 drinks 1 2 3 5 6 61 62 63 64 65 66
Prisoner 15 drinks 1 2 3 4 6 61 62 63 64 65 66
Prisoner 16 drinks 1 2 3 4 5 61 62 63 64 65 66
Prisoner 17 drinks 1 2 3 4 5 6 62 63 64 65 66
Prisoner 18 drinks 1 2 3 4 5 6 61 63 64 65 66
Prisoner 19 drinks 1 2 3 4 5 6 61 62 64 65 66
Prisoner 20 drinks 1 2 3 4 5 6 61 62 63 65 66
Prisoner 11 drinks 1 2 3 4 5 6 61 62 63 64 66
Who dies: 1 12 14 15 16 17 18 20 11
Prisoner 1 dies
Prisoner 11 dies
Prisoner 12 dies
Prisoner 14 dies
Prisoner 15 dies
Prisoner 16 dies
Prisoner 17 dies
Prisoner 18 dies
Prisoner 20 dies
Suspected bottles: 3 63

无需人工输入即可进行测试

$ perl poisened_bottles.pl 7 2 5
Prisoners: 7
Bottles: 12
Prisoner 1 drinks 3 4 5 6 7 8 9 10 11 12
Prisoner 2 drinks 1 2 5 6 7 8 9 10 11 12
Prisoner 3 drinks 1 2 3 4 7 8 9 10 11 12
Prisoner 4 drinks 1 2 3 4 5 6 9 10 11 12
Prisoner 5 drinks 1 2 3 4 5 6 7 8 11 12
Prisoner 6 drinks 1 2 3 4 5 6 7 8 9 10
Prisoner 2 dies
Prisoner 4 dies
Prisoner 5 dies
Prisoner 6 dies
Prisoner 1 drinks 2 5 6
Prisoner 7 drinks 1 5 6
Prisoner 3 drinks 1 2 6
Prisoner 1 dies
Suspected bottles: 2 5

2

按照传统,我将发布最后一个参考答案。

Python — 7瓶

prisoners = int(raw_input())

bottles = 0
while (bottles * (bottles + 1) / 2 - 1) <= prisoners:
    bottles += 1

print bottles

pairs = []
for i in range(bottles):
    for j in range(i + 1, bottles):
        pairs += [str(i + 1) + " " + str(j + 1)]

for i in range(prisoners):
    if i < len(pairs):
        print pairs[i]
    else:
        print

dead_prisoner = raw_input()

for i in range(prisoners):
    print
raw_input() # discard the second day entirely

if dead_prisoner == "":
    print pairs[-1]
else:
    print pairs[int(dead_prisoner) - 1]

让每个囚犯喝一瓶可能的瓶子,最后两瓶除外。如果有任何囚犯死亡,那名囚犯喝的那对是中毒的。否则,这是最后两个被毒死的瓶子。

对于至少要分配的n(n-1)/2 - 1囚犯,您最多可以n配药。对于n = 7此下限为20

我们只有真正需要的一个天这一解决方案的工作。范围类似的两天解决方案最多可得到20瓶N = 20,但是对于一个简单的答案来说,这太繁琐了。

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.