Занадто багато шпигунів!


38

Ви воюєте з розгалуженою мережею ворожих шпигунів . Ви знаєте, що кожен шпигун має принаймні одну (іноді множину) фальшивих посвідчень, якими він любить користуватися. Ви дуже хотіли б дізнатися, з якими шпигунами ви насправді маєте справу.

На щастя, ваші контррозвідувальні агенти виконують свою роботу і іноді можуть з’ясувати, коли два фальшиві посвідчення фактично контролюються тим самим ворожим шпигуном.

Тобто:

  • Однак ваші агенти не завжди знають, коли два фальшиві ідентифікатори мають одного шпигуна за ними
  • Якщо агент повідомляє вам, що двоє підроблених ідентичностей контролюються тим самим шпигуном, ви довіряєте, що вони праві.

Повідомлення агента

Агенти надсилають вам криптовалютні повідомлення, в яких повідомляють, які особи мають одного шпигуна за ними. Приклад:

У вас є два агенти та 5 підроблених ідентичностей для вирішення.

Перший агент надсилає вам повідомлення:

Red Red Blue Orange Orange

Це означає, що вони думають, що є три шпигуни:

  • перший (червоний) керує особами 1 і 2
  • другий (Blue) керує ідентичністю 3
  • третій (помаранчевий) керує особами 4 та 5

Другий агент надсилає вам повідомлення:

cat dog dog bird fly

Це означає, що вони думають, що є 4 шпигуни:

  • перший (кіт) контролює особу 1
  • другий (собака) керує особами 2 і 3
  • третій (птах) контролює особистість 4
  • четвертий (муха) контролює особу 5

Складаючи дані, ми бачимо:

Identities:   id1    id2    id3    id4    id5 
Agent 1:    |--same-spy--|       |--same-spy--|
Agent 2:           |--same-spy--|
Conclusion: |-----same-spy------||--same-spy--|

Це означає, що є максимум 2 шпигуни .

Примітки

Особи, які належать одному шпигуну, не повинні бути послідовними, тобто повідомлення типу:

dog cat dog

є дійсним.

Крім того, те саме слово можуть використовуватися двома різними агентами - це нічого не означає, це просто збіг, наприклад:

Agent 1: Steam Water Ice
Agent 2: Ice Ice Baby

Лід використовується обома агентами - Iceвикористовуваний перший агент не пов'язаний з двома випадками Iceвикористання другого агента.

Виклик

Складіть усі дані своїх агентів і з’ясуйте, скільки насправді є ворожих шпигунів. (Якщо точніше, отримайте нижню верхню межу, враховуючи обмежену інформацію, яку ви маєте.)

Виграє найкоротший код у байтах.

Специфікація вводу та виводу

Вхід - це список з n рядків, які представляють n повідомлень від агентів. Кожен рядок складається з k токенів, розділених пробілом, однакового k для всіх рядків. Маркери буквено-цифрові, довільної довжини. Справа має значення.

Вихід повинен бути єдиним числом, що представляє кількість різних шпигунів, виходячи з інформації ваших агентів.

Приклади

Приклад 1

Вхід:

Angel Devil Angel Joker Thief Thief
Ra Ra Ras Pu Ti N
say sea c c see cee

Вихід:

2

Приклад 2

Вхід:

Blossom Bubbles Buttercup
Ed Edd Eddy

Вихід:

3

Приклад 3

Вхід:

Botswana Botswana Botswana
Left Middle Right

Вихід:

1

Приклад 4

Вхід:

Black White
White Black

Вихід:

2

Приклад 5

Вхід:

Foo Bar Foo
Foo Bar Bar

Вихід:

1

Приклад 6

Вхід:

A B C D
A A C D
A B C C
A B B D

Вихід:

1

Приклад 7

Вхід:

A B A C

Вихід:

3

Приклад 8

Вхід:

A
B
C

Вихід:

1

Приклад 9

Вхід:

X

Вихід:

1

Чи можемо ми взяти кожен рядок як масив слів?
Арнольд

8
@HenryHenrinson Єдине, що робить введення строгим - це додати коротке розмиття на початку коду, щоб змінити формат введення. Це насправді нічого не додає до самого виклику
fəˈnɛtɪk

6
Мені здається, що це дасть більше можливостей
пограти

17
Суворі формати вводу / виводу дуже не враховують, оскільки вони загрожують суті завдання. Наприклад, не вимагати, щоб введення даних було у вигляді рядків, розділених пробілами, не є необхідним, оскільки кожний рядок також може представляти як список слів (що сказав Арнольд), і єдине, що це правило додає до виклику це необхідність розділити лінії, те, що не обов'язково є частиною виклику.
Ерік Аутгольфер

2
Цей заголовок звучить як ваша середня гра Team Fortress 2!
Tvde1

Відповіді:


10

Кувалда 0.5.1 , 16 15 байт

⡡⠥⡀⡾⠥⢢⠍⣽⡷⣩⣅⡷⣡⢒⠅

Декомпресується у цю функцію мови Wolfram (фінал &неявний):

Length[ConnectedComponents[RelationGraph[Inner[Equal, ##1, Or] &,
    Transpose[StringSplit @ #1]]]] &

Спробуйте в Інтернеті!

Transpose[StringSplit @ #1]: Розділіть кожен рядок у списку вводу та візьміть стовпці (ідентифікації шпигуна)

RelationGraph[Inner[Equal, ##1, Or] &, ...]: Побудуйте графік, де дві вершини ділять ребро, якщо принаймні одна позиція є рівною (якщо якийсь дружній агент їх класифікує як одного шпигуна)

Length[ConnectedComponents[...]]: Кількість підключених компонентів - це верхня межа можливої ​​кількості шпигунів.


9

JavaScript (Node.js) ,  155 150 142  141 байт

a=>new Set((a=a.map(s=>s.split` `))[0].map((_,x)=>a.flat(m=1<<x).map(o=_=>a.map((b,y)=>b.map((w,i)=>m>>i&1|o[w+=y]?o[w]=m|=1<<i:0)))|m)).size

Спробуйте в Інтернеті!

Як?

хмх

+---------+-------+-------+-------+-------+-------+-------+
| x       |   0   |   1   |   2   |   3   |   4   |   5   |
+---------+-------+-------+-------+-------+-------+-------+
| 2**x    |   1   |   2   |   4   |   8   |  16   |  32   |
+---------+-------+-------+-------+-------+-------+-------+
| words   | Angel | Devil | Angel | Joker | Thief | Thief |
|         | Ra    | Ra    | Ras   | Pu    | Ti    | N     |
|         | say   | sea   | c     | c     | see   | cee   |
+---------+-------+-------+-------+-------+-------+-------+
| bitmask |  15   |  15   |  15   |  15   |  48   |  48   |
+---------+-------+-------+-------+-------+-------+-------+

Прокоментував

a =>                      // a[] = input
new Set(                  // we eventually convert the generated array into a set
  (a = a.map(s =>         // we first need to convert each line into
    s.split` `            // an array of words (*sigh*)
  ))                      //
  [0].map((_, x) =>       // for each word at position x in the first line:
    a.flat(m = 1 << x)    //   initialize a bitmask m with the x-th bit set and build an
                          //   array containing as many entries (N) as there are words in
                          //   the whole matrix
    .map(o =              //   the object o is used to store words
         _ =>             //   repeat N times to ensure that all relations are found:
      a.map((b, y) =>     //     for each line b[] at position y in a[]:
        b.map((w, i) =>   //       for each word w at position i in b[]:
          m >> i & 1 |    //         if the i-th bit is set in m (the relation already
                          //         exists)
          o[w += y] ?     //         or w + y is set in o (a relation exists in this line):
            o[w] =        //           set o[w + y] (the value doesn't matter as long as
                          //           it's non-zero)
              m |= 1 << i //           set the i-th bit in m
          :               //         else:
            0             //           do nothing
        )                 //       end of map() over the words
      )                   //     end of map() over the lines
    ) | m                 //   end of map() over all flatten entries; yield m
  )                       // end of map() over x
).size                    // return the size of the corresponding set

Отже ... на практиці це матиме обмеження щодо ідентичності 32 або 64?
Vilx-

@ Vilx - Я думаю, він міг би перейти на BigInt, хоча це, звичайно, коштувало б байтів.
Ніл


6

Пітон 3 , 132 162 154 139 135 байт

def f(a):r=[*zip(*[map(b.index,b)for b in map(str.split,a)])];return sum(i==min(min(u)for u in r if min(w)in u)for i,w in enumerate(r))

Спробуйте в Інтернеті!

Це дуже компактна реалізація графічного алгоритму, що ідентифікує кластери.

  1. Для кожного агента, ми створюємо карту профілів і їх псевдонім, який є найнижчим показником появи: [map(b.index,b)for b in map(str.split,a)]. Тобто [0,1,2,1,2]ототожнюють трьох шпигунів, де перший профіль належить одному, другий і четвертий - іншому, а третій і п'ятий - останньому. Індекс групи - це також індекс першого профілю в групі.

  2. Перекладаючи цю матрицю ( [*zip(*m...)]), ми отримуємо групове членство для кожного профілю. Це утворює спрямований, ациклічний графік, оскільки групові індекси є підмножиною профільних індексів, і всі ребра йдуть у бік нижчих або рівних індексів. Профілі, що відповідають одному і тому ж шпигуну, тепер утворюють кластер без з'єднань з іншими профілями. Однак у нас все ще є дублікати шляхів, оскільки індекси профілів пов'язані з індексами декількох груп.

  3. За допомогою наступних циклів ми мінімізуємо графік до плоского лісу, де всі профілі пов'язані безпосередньо з найнижчим показником у їхньому дереві, тобто коренем: min(min(u)for u in r if min(w)in u)

  4. І, нарешті, повертає кількість коренів в лісі, тобто показники , пов'язані з собою: return sum(i==...).


чи потрібен відступ? З тих пір, як я використовував python, минуло століття, але я, мабуть, пам’ятаю, що ви можете зробити oneliners.
Марк Гарднер

Можна, але не, якщо ви використовуєте вкладені для циклів. TIO для себе;)
movatica

5

Вугілля деревне , 49 43 байт

≔⪪S θWS«≔⪪ι ιFLιUMθ⎇⁼λ§θκ§θ⌕ι§ικλ»ILΦθ⁼κ⌕θι

Спробуйте в Інтернеті! Посилання на багатослівну версію коду. Можливо, можна зберегти пару байтів, використовуючи громіздкий формат введення. Пояснення:

≔⪪S θ

Введіть список першого агента.

WS«

Повторіть для решти агентів.

≔⪪ι ι

Введіть їх список.

FLι

Переведіть петлю на кожен індекс елемента.

UMθ⎇⁼λ§θκ§θ⌕ι§ικλ»

Знайдіть перший елемент у списку цього агента з однаковою ідентичністю та оновіть список першого агента, щоб показати, що вони однакові.

ILΦθ⁼κ⌕θι

Підрахуйте кількість унікальних особи, що залишилися.


5

Желе , 25 15 байт

ḲĠ)ẎfƇFQɗⱮQ$ÐLL

Спробуйте в Інтернеті!

Монадійне посилання, що містить список претензій агента, що розділяє простір, і повертає нижню верхню межу кількості різних шпигунів.

Пояснення

  )              | For each list:
Ḳ                | - Split at spaces
 Ġ               | - Group indices of equal items
   Ẏ             | Tighten lists, so we have a single list of grouped indices
           $ÐL   | Repeat the following until no change:
        ʋⱮQ      | - Do the following as a dyad, mapping through each element of the uniquified list as the right argument
    fƇ           |   - Keep only those list members with one or more items matching the right argument
      F          |   - Flatten
       Q         |   - Uniquify
              L  | Finally take the length of the resultant list

Дякуємо @Arnauld та @JonathanAllan за виявлення проблем із попередніми версіями, а також @JonathanAllan знову за збереження байта! Якщо специфікацію введення було розслаблено, щоб дозволити список списків, це дозволить зберегти один байт.


Я думаю, що сортування може насправді бути непотрібним, оскільки індекси у групах з Ġсортуються, а результат згладженого, fƇFQповторного дублювання фільтрів завжди після повторного застосування закінчується цим у відсортованому порядку (наприклад 'a a b b c', 'a b a b c, не знайдемо можливого [3,4,1,2], навіть якщо це з'явиться по дорозі). Так ḲĠ)ẎfƇFQɗⱮQ$ÐLLможе бути добре для 15?
Джонатан Аллан

@JonathanAllan гарне місце. Я трохи розіграв (і подумайте, як це працює) і вважаю, що ви праві.
Нік Кеннеді

4

JavaScript (Node.js) , 120 байт

a=>a.map(l=>(s=l.split` `).map((w,i)=>r[o(i)]=o(s.indexOf(w)),o=i=>r[i]-i?o(r[i]):i),r=[])|r.map(g=(v,i)=>t+=v==i,t=0)|t

Спробуйте в Інтернеті!

a=>a.map(l=>(                  // for each line
  (s=l.split` `).map((w,i)=>(  // for each words in line
    r[o(i)]=o(s.indexOf(w)),   // join(current index, first occurrence index)
  )),                          //   without updating nodes in path
  o=i=>r[i]-i?o(r[i]):i,       // a function to find root of some node
  r=[]                         // initial disjoint-set
))|
r.map(g=(v,i)=>t+=v==i,t=0)|   // count roots of tree
t                              // output

3

Лушпиння , 12 байт

LωomΣknṁoηkw

Спробуйте в Інтернеті!

Пояснення

Ідея полягає у створенні списку всіх груп шпигунів, які, як відомо, є однією людиною, а потім поступово зливаються пересічні групи до досягнення фіксованої точки. Вихід - це кількість груп, що залишилися, які неможливо було об'єднати.

LωomΣknṁoηkw  Implicit input: list of strings, say ["a bc a","b g g"]
       ṁ      Map and concatenate:
           w   Split at spaces: "a bc a" becomes ["a","bc","a"]
         ηk    Group indices by equality of elements: [[1,3],[2]]
              Result: [[1,3],[2],[1],[2,3]]
 ω            Iterate until result doesn't change:
     k         Group greedily by
      n        (non-emptiness of) intersection: [[[1,3],[1]],[[2],[2,3]]]
   mΣ          Concatenate each part: [[1,3,1],[2,2,3]]
              Result: [[1,3,1,2,2,3]]
L             Length: 1


3

Ruby , 123 117 байт

Використовує подібну ідею до рішення Python 3 movatica, але обчислює найнижчий індекс шпигуна для кожного "дерева" дещо по-іншому (відстежуючи попередні зустрічаються профілі, знаходячись накладок, якщо він існує, та комбінуючи їх)

-6 байт від @GB.

->a,*b{a.map{|s|e=s.split;e.map{|i|e.index i}}.transpose.map{|e|b<<(b.find{|i|i-e!=i}||[])+e}
b.map(&:min).uniq.size}

Спробуйте в Інтернеті!

Пояснення

->a,*b{                                             # Start lambda with input a, b=[]
       x=
         a.map{|s|                             }    # For each agent's report
                  e=s.split;                        # Split the words
                            e.map{|i|e.index i}     # Get spy number for each

   .transpose                                       # Transpose to get group membership
             .map{|e|                            }  # For each profile
                        (b.find{|i|i-e!=i}||[])     # Find a profile in b that overlaps
                                                    #  If one is not found, use []
                                               +e   # Add the profile onto the found one
                     b<<                            # Insert this modified profile into b

b.map(&:min)                                        # Get minimum of each modded profile
            .uniq                                   # Deduplicate
                 .size                              # Size of array
}                                                   # Implicit return

Замість того, щоб спливати та блискавки, ви можете просто транспонувати.
ГБ


@GB спасибі за голову вгору; Я використовую pop-zip або shift-zip, щоб назавжди перемістити масиви! Також ваш трюк у використанні s.split.map{|i|s.index i}є приємним, але він може створювати крайові корпуси залежно від довжини входів. Цей вхід повинен повернути 3, а не 2.
Значення чорнила

2

Пітон 2 , 229 221 байт

e=enumerate
def f(s):
 v=[];u=sum([(lambda a:[{i for i,x in e(a)if x==k}for k in set(a)])(a.split())for a in s.split('\n')],v)
 while u:
	x=u.pop()
	for i,y in e(u):
	 if x&y:u.pop(i);u+=[x|y];break
	else:v+=[x]
 return v

Спробуйте в Інтернеті!

8 байт thx в wilkben .


Оскільки gвикористовується лише один раз, ви не могли б визначити його вбудованим? Я якось забуваю, якщо це можливо в Python, але я, мабуть, пам’ятаю, що це так.
Стівен


1

Чисто , 137 байт

import StdEnv,Text,Data.List
q=length
$l=q(iter(q l)(map flatten o groupBy isAnyMember)(transpose[[(s,n)\\s<-split" "z]\\z<-l&n<-[1..]]))

Спробуйте в Інтернеті!

Пов’язує рядки, які використовуються агентами, з номером рядка, який з'являється у, щоб запобігти рівність між агентами, а потім повторно перевіряє, чи перекриваються фрази для будь-якої позиції та підраховує кількість отриманих наборів.


0

PHP , 271 байт

Це не спрацює, якщо будь-яка особа - це лише цифри, оскільки я зберігаю "номер шпигуна" в ідентифікаціях. Я не думаю, що це було б важко виправити.

$a=$argv;array_shift($a);if(count($a)==1)array_push($a,...$a);foreach($a as&$b)$b=explode(" ",$b);$c=array_map(null,...$a);foreach($c as&$d)foreach($d as$k=>$e){if(!$d[s])$d[s]=++$s;foreach($c as&$f)if($f[$k]==$e)$f[s]=$d[s];}echo count(array_unique(array_column($c,s)));

Спробуйте в Інтернеті!

Пояснення

Я щось плутаю, коли пишу це, але це працює для всіх тестових випадків!

$a=$argv;					//shorten the arguments variable
array_shift($a);				//removes the script name from the arguments variable
if(count($a)==1)array_push($a,...$a);		//the code needs at least 2 messages to run so if only 1 message duplicate it. "..." passes the stuff in the array rather than the array itself?
foreach($a as&$b)$b=explode(" ",$b);		//turns each string message into an array
$c=array_map(null,...$a);			//if you give array_map "null" for the callabck then it zips the arrays, turning a m by n 2D array into a n by m 2D array. this changes it from the messages being grouped to the identities being grouped
foreach($c as&$d)				//loop over the groups of identities
	foreach($d as$k=>$e)			//loop over the names the agents gave the identity and keep track of the key
	{
		if(!$d[s])$d[s]=++$s;		//if this identity doesn't have a "spy number" give it the next one
		foreach($c as&$f)		//loop over the groups of identities again
			if($f[$k]==$e)		//check if the agents gave any other identities this name 
				$f[s]=$d[s];	//if they did then give those the same "spy number"
	}
echo count(array_unique(array_column($c,s)));	//use array_column to get the "spy number" of each identity, remove duplicates using array_unique and then count the size of the array giving the upper limit of spies

Спробуйте в Інтернеті!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.