Построить планировщик тестирования отравленных вин

16

Недавно в Puzzling.SE я писал о проблеме, касающейся определения того, какие две бутылки из большего числа отравлены, когда яд активируется, только если оба компонента выпиты. В итоге это стало настоящим испытанием, и большинству людей удалось довести его до 18 или 19 заключенных, используя совершенно разные алгоритмы.

Первоначальное постановка задачи состоит в следующем:

Вы правитель средневекового королевства, который любит устраивать вечеринки. Придворный, который пытался отравить одну из твоих бутылок вина в прошлый раз, был в ярости, узнав, что тебе удалось определить, какую бутылку он отравил из 1000 с десятью заключенными.

На этот раз он немного хитрее. Он разработал сложный яд P : бинарную жидкость, которая смертельна только тогда, когда смешиваются два отдельных безвредных компонента; это похоже на то, как работает эпоксидная смола. Он отправил вам еще один ящик из 1000 бутылок вина. Одна бутылка имеет компонент, C_aа другая имеет компонент C_b. ( P = C_a + C_b)

Любой, кто пьет оба компонента, умрет в полночь в ночь, когда выпил последний компонент, независимо от того, когда в день они пили жидкость. Каждый ядовитый компонент остается в организме до тех пор, пока не активируется второй компонент, поэтому, если вы пьете один компонент один день, а другой - следующий, вы умрете в полночь в конце второго дня.

У вас есть два дня до вашей следующей вечеринки. Какое минимальное количество заключенных вам нужно использовать для тестирования, чтобы определить, какие две бутылки испорчены, и какой алгоритм вы должны использовать с таким количеством заключенных?


Бонус
Кроме того, предположим, что в вашем распоряжении установлен фиксированный лимит в 20 заключенных, какое максимальное количество бутылок вы можете теоретически протестировать и прийти к точному выводу о том, какие бутылки были затронуты?

Ваша задача - построить программу для решения бонусной проблемы. Учитывая nзаключенных, ваша программа разработает график тестирования, который сможет обнаружить две отравленные бутылки среди mбутылок, mнасколько это возможно.

Ваша программа первоначально будет принимать в качестве входных данных Nчисло заключенных. Затем он выведет:

  • Mколичество бутылок, которое вы попытаетесь проверить. Эти бутылки будут маркированы от 1до M.

  • N строки, содержащие этикетки бутылок, которые выпьет каждый заключенный.

Затем ваша программа будет принимать в качестве входных данных, какие заключенные умерли в первый день, с заключенным в первой строке 1, следующей строкой 2и т. Д. Затем будет выведено:

  • Nбольше строк, содержащих этикетки бутылок, которые выпьет каждый заключенный. У мертвых заключенных будут пустые строки.

Затем ваша программа примет в качестве входных данных, какие заключенные умерли на второй день, и выведет два числа Aи B, представляющих, какие две бутылки, по вашему мнению, содержат яд.

Пример ввода для двух заключенных и четырех бутылок может быть таким, если бутылки 1и 3отравлены:

> 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и последовательно более высокие кейсы после этого.

  • Длина кода. (Короче код выигрывает.)

Джо З.
источник
Как будут выглядеть данные, если более одного заключенного умирает в один день? Ни один из ваших примеров не охватывает этот случай, и спецификация для меня неоднозначна.
Эсултаник
Это одна строка с разделенным пробелами списком погибших?
Эсултаник
Более короткий код имеет значение больше, чем количество бутылок? Производительно ли увеличивать длину кода, чтобы он обрабатывал еще одну бутылку, как я делал в своем недавнем редактировании?
pppery
Количество бутылок имеет приоритет. Если вы сделаете свой код длиннее и сложнее, чтобы втиснуть больше бутылок, это будет продуктивно.
Джо З.
В исходной задаче есть всего 2 дня, чтобы решить проблему. Это также правило для вызова? (это жестко ограничивает возможные решения, однако неограниченное количество дней будет легким)
LukStorms

Ответы:

7

Python 2.7.9 - 21 бутылка

Предполагая, что Е.С. Султаник прав в том, что является входом, когда умирают несколько заключенных

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])

Алгоритм: каждый заключенный пьет из каждой бутылки, кроме их количества (1-й заключенный не пьет первую бутылку). Если они не умирают, их номерная бутылка отравлена. Если выживет только один заключенный, дополнительная бутылка отравлена.

pppery
источник
3

Perl 5 , 66 бутылок

(72 бутылки на 21 заключенного)

Заключенные оптимально поделены на 2 группы. Бутылки сгруппированы в наборы.

Каждый заключенный группы 1 будет пить из всех сетов, кроме одного. Там будет 1 или 2 выживших. 1 или 2 сета, которые они не выпили, будут продолжаться до второго дня.

На второй день оставшиеся заключенные (включая выживших) пьют из всех оставшихся бутылок, кроме одной.
Когда выживают 2 заключенных, бутылки, которые они не пили, отравляются.
Если остается только 1 заключенный, то бутылка, которую они все выпили, также является подозрительной.

Код включает дополнительную функциональность для облегчения тестирования. Когда добавленные бутылки добавляются в качестве дополнительных параметров, он не будет запрашивать информацию о том, кто умер.

($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
LukStorms
источник
2

По традиции я выложу последний справочный ответ.

Питон - 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, но это слишком много работы для тривиального ответа.

Джо З.
источник