A game of locks and keys

12

1

There are n boxes, numbered 1-n. Each box is locked, such that it can be opened by only one corresponding type of key (also numbered 1-n). These keys are randomly scattered in the boxes (one box may have any number of keys, one key may have any number of duplicates), and then all boxes are shut. A treasure (numbered 0) has also been locked in many of the boxes.

You have hired a locksmith to retrieve all the treasure. He charges for each box he cracks open. There is no charge for opening a box for which the key is already available.

Input is the contents of each box. You can decide the format of the input.

Output the minimum cost required to get the treasures.

Notes

  1. Your algorithm may take a long time, but that is irrelevant.
  2. Shortest code wins.
  3. No need to bother about invalid input.

Sample data

Here line i represents the keys present in box i.

Input

2 0
3
4 0
5 6 0
6
0

Output

1

Input

2 0
3 0

4 0
6
5 0

Output

3

Input

2 4 0
3 0

1 0
6
5 0

Output

2

Input

1
3 4


2 6
5

Output

0

ghosts_in_the_code

Posted 2015-11-19T15:46:32.650

Reputation: 2 907

2

Is this perhaps related to this?

– Addison Crump – 2015-11-19T15:51:58.647

Also related: http://puzzling.stackexchange.com/questions/23150/how-to-beat-count-dracula

– Leif Willerts – 2015-11-19T16:10:58.473

@VoteToClose Nice video. It is similar, except that it talks of a mathematical puzzle and specific algorithm, rather than a generalised one. – ghosts_in_the_code – 2015-11-19T17:47:12.090

@VoteToClose, not really. That question is about permutations; this question is graph reachability plus smallest covering set. – Peter Taylor – 2015-11-19T18:55:16.693

1

This seems related to this puzzle about 100 locked boxes of wood and steel: http://puzzling.stackexchange.com/q/17852/4551

– xnor – 2015-11-20T04:31:04.793

I'm not a fan of the specific input format. I expect I'd need a decent fraction of my code just to put it in a usable form. In the future, I suggest allowing more flexible forms of input for algorithmic style problems. – xnor – 2015-11-20T04:34:33.437

@xnor The input is simple only. How can I make it simpler than this? – ghosts_in_the_code – 2015-11-20T10:30:57.203

4@ghosts_in_the_code It's not about simplicity but about flexibility. Commonly, challenges that require structured input allow any convenient list format, as long as the data is not preprocessed. Depending on the language that could mean a whitespace separated file like you have, or it could mean [[1] [3 4] [] [] [2 6] [5]] or maybe {{1},{3,4},{},{},{2,6},{5}}. This way, most languages can reduce reading the input to something as trivial as i=eval(read()) and focus on the fun part of the challenge. – Martin Ender – 2015-11-20T16:55:04.570

I am missing something to understand this puzzle. How does the last one work to zero if we don't start with any keys and locksmith charges to open a box we don't already have the key for? – ŽaMan – 2015-11-20T23:22:29.453

@qumonio Only all the treasure (0s) need to be found. Since there are no 0s, we have already completed the challenge even before opening any box. – ghosts_in_the_code – 2015-11-21T07:30:18.713

@xnor @ MartinButtner Done. – ghosts_in_the_code – 2015-11-21T07:34:39.993

Answers

6

CJam, 59 52 50 49 45 43 42 bytes

qN/ee::~e!{_0+{0a&}#>W%_{1$|(z@-},,\;}%:e<

Thanks to @MartinBüttner for golfing off 3 bytes and paving the way for 4 more!

Try it online in the CJam interpreter.

How it works

qN/      e# Read all input and split it at linefeeds.
ee       e# Enumerate the lines.
         e# STACK: [[0 "i0 i1 ..."] [1 "j0 j1 ..."] ...]
::~      e# Apply ~ (bitwise NOT/evaluate) to each item of the pairs.
         e# STACK: [[-1 i0 i1 ...] [-2 j0 j1 ...] ...]
e!       e# Push all unique permutations of the resulting array.
{        e# For each permutation:
  _0+    e#   Push a copy and append 0 to it.
  {0a&}# e#   Find the first index of an element that contains 0.
  >      e#   Discard all previous elements of the array.
  W%     e#   Reverse the resulting array.
         e#   We now have a (partial) permutation that contains
         e#   all treasures and ends with a treasure.
  _      e#   Push a copy. The original (which contains lists, but no 
              numbers) will serve as accumulator.
  {      e#   Filter; for each list in the array:
    1$|  e#     Push a copy of the accumulator and perform set union.
    (    e#     Shift out the first element (bitwise NOT of 0-based index).
    z    e#     Apply absolute value to push the 1-based index.
    @-   e#     Perform set difference with the former state of the 
         e#     accumulator. This pushes an empty list iff the 1-based
         e#     index was already in the accumulator, i.e., iff we already
         e#     had a key.
  },     e#   Keep the element if we did not have the key.
  ,      e#   Count the kept elements.
  \;     e#   Discard the accumulator from the stack.
}%       e#
:e<      e# Get the minimum of all results.

Dennis

Posted 2015-11-19T15:46:32.650

Reputation: 196 637

2Could you add an explanation for us without the gift of CJam understanding? :D I'd like to know how this works. – Addison Crump – 2015-11-20T09:09:53.830

2

@VoteToClose Look at this CJAM101

– user41805 – 2015-11-20T17:18:40.657

array long & works, so you can remove the a from 0a&. Sadly this makes it slightly harder to catch you. – Peter Taylor – 2015-11-20T23:16:36.080

@PeterTaylor Unfortunately, if I replace 0a& with 0&, I also have to replace 0+ with 0aa+, since 0 0& is falsy. – Dennis – 2015-11-21T00:42:39.217

@VoteToClose I've edited my answer. – Dennis – 2015-11-21T04:24:20.683

@qumonio Each box in your example contains the key for the next one, so you only have to pay to open the first. – Dennis – 2015-11-23T07:32:33.190

@Dennis I am sorry, I had some linkage trouble and I meant to provide input similar to this JS array : [ [4,0], [1,3,4], [0], [6,0], [3,0], [5]] I think I got it set properly here http://cjam.aditsu.net/#code=qN%2Fee%3A%3A~e%21%7B_0%2B%7B0a%26%7D%23%3EW%25_%7B1%24%7C(z%40-%7D%2C%2C%5C%3B%7D%25%3Ae%3C&input=4%200%0A1%203%204%0A0%0A6%200%0A3%200%0A5

– ŽaMan – 2015-11-23T07:40:07.783

@qumonio Paying for box 2 gives you keys to 1, 3, and 4. Box 4 contains a key to 6 which, in turn, contains a key to 5. – Dennis – 2015-11-23T07:47:42.533

brilliant. I think this puzzle is too much for me. congrats on the win :) – ŽaMan – 2015-11-23T07:50:57.077

2

CJam (53 bytes)

Nq+N/:a::~:A,_m*_.&{,}$_{{_Af=e_|}:PA,*A,,^0-P0&!}#=,

This is rather too slow for the online interpreter.

Dissection

Nq+N/:a::~:A      e# Parse the input into arrays and store in A
,_m*_.&           e# Generate (with duplicates) a powerset of [0 1 ... n]
{,}$              e# Sort by size
_{                e# Create a copy and search for first index satisfying...
  {_Af=e_|}:P     e#   Store in P a block which does a reachability expansion
  A,*             e#   Apply it n times (no path can be longer than n)
  A,,^0-          e#   Invert to get the unreached nodes (except 0)
  P               e#   Apply P again to see what's reached from the unreached nodes
  0&!             e#   Check that it doesn't include [0]
}#
=,                e# Look up the powerset element at that index and find length

Peter Taylor

Posted 2015-11-19T15:46:32.650

Reputation: 41 901

I received java.lang.OutOfMemoryError: Java heap space with your program. – ŽaMan – 2015-11-23T06:06:08.387

@qumonio, it's not particularly scalable. I haven't tested it with inputs larger than the test inputs in the question, so I'm not sure how far it can go on a standard 1GB heap. – Peter Taylor – 2015-11-23T07:14:35.583

I was trying the 6 line here shown as an array in JS: [ [4,0], [1,3,4], [0], [6,0], [3,0], [5]] of course with the input style as shown in the original post. – ŽaMan – 2015-11-23T07:16:37.650

@qumonio, on my computer it handles that input fine with only 128MB of heap, which is less than it has by default. – Peter Taylor – 2015-11-23T22:22:33.920

0

Haskell, 173 bytes

l is the one you wanna call.

Not sure whether I shouldn't use a pseudo-Map instead ([(Int,[Int])] instead of [[Int]]).

l=o[].map(map read).map words.lines
o[]b|0`notElem`concat b=0|0<1=1+minimum[o[n]b|n<-[1..length b],b!!(n-1)/=[]]
o(n:k)b=o(filter(/=0)(k++b!!(n-1)))(take(n-1)b++[]:drop n b)

Leif Willerts

Posted 2015-11-19T15:46:32.650

Reputation: 1 060