¡Demasiados espías!

38

Estás luchando contra una extensa red de espías enemigos . Sabes que cada espía tiene al menos una (a veces múltiples) identidades falsas que les gusta usar. Realmente te gustaría saber con cuántos espías estás lidiando realmente.

Afortunadamente, tus agentes de contrainteligencia están haciendo su trabajo y, a veces, pueden descubrir cuándo dos identidades falsas son controladas por el mismo espía enemigo.

Es decir:

  • Sin embargo, sus agentes no siempre saben cuándo dos identidades falsas tienen el mismo espía detrás de ellos.
  • Si un agente le dice que dos identidades falsas están controladas por el mismo espía, usted confía en que tienen razón.

Mensajes del agente

Los agentes le envían mensajes crípticos que le dicen qué identidades tienen el mismo espía detrás de ellos. Un ejemplo:

Tienes 2 agentes y 5 identidades falsas para tratar.

El primer agente te envía un mensaje:

Red Red Blue Orange Orange

Esto significa que piensan que hay 3 espías:

  • el primero (rojo) controla las identidades 1 y 2
  • el segundo (Azul) controla la identidad 3
  • el tercero (Naranja) controla las identidades 4 y 5

El segundo agente te envía un mensaje:

cat dog dog bird fly

Esto significa que piensan que hay 4 espías:

  • el primero (gato) controla la identidad 1
  • el segundo (perro) controla las identidades 2 y 3
  • el tercero (pájaro) controla la identidad 4
  • el cuarto (mosca) controla la identidad 5

Compilando la información que vemos:

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

Esto significa que hay como máximo 2 espías .

Notas

Las identidades propiedad del mismo espía no tienen que ser consecutivas, es decir, un mensaje como:

dog cat dog

es válido.

Además, dos agentes diferentes pueden usar la misma palabra; eso no significa nada, es solo una coincidencia, por ejemplo:

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

El hielo es usado por ambos agentes: el Iceusado por el primer agente no está relacionado con las dos ocurrencias del Iceusado por el segundo agente.

Reto

Recopila la información de todos tus agentes y calcula cuántos espías enemigos hay realmente. (Para ser más precisos, obtenga el límite superior más bajo, dada la información limitada que tiene).

El código más corto en bytes gana.

Especificaciones de entrada y salida

La entrada es una lista de n líneas, que representan n mensajes de agentes. Cada línea consta de k fichas separadas por espacios, la misma k para todas las líneas. Los tokens son alfanuméricos, de longitud arbitraria. El caso importa.

El resultado debe ser un número único, que represente el número de espías distintos, según la información de sus agentes.

Ejemplos

Ejemplo 1

Entrada:

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

Salida:

2

Ejemplo 2

Entrada:

Blossom Bubbles Buttercup
Ed Edd Eddy

Salida:

3

Ejemplo 3

Entrada:

Botswana Botswana Botswana
Left Middle Right

Salida:

1

Ejemplo 4

Entrada:

Black White
White Black

Salida:

2

Ejemplo 5

Entrada:

Foo Bar Foo
Foo Bar Bar

Salida:

1

Ejemplo 6

Entrada:

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

Salida:

1

Ejemplo 7

Entrada:

A B A C

Salida:

3

Ejemplo 8

Entrada:

A
B
C

Salida:

1

Ejemplo 9

Entrada:

X

Salida:

1
Henry Henrinson
fuente
¿Podemos tomar cada línea como un conjunto de palabras?
Arnauld
8
@HenryHenrinson Lo único que hace que la entrada sea estricta es agregar una breve reseña al comienzo del código para cambiar el formato de entrada. Realmente no agrega nada al desafío en sí mismo
fəˈnɛtɪk
66
Me parece que eso dará más oportunidades para jugar golf el código :)
Henry Henrinson
17
Los formatos estrictos de E / S se desaconsejan realmente, ya que restan valor al núcleo del desafío. Por ejemplo, exigir que la entrada esté en forma de líneas de palabras separadas por espacios no es necesario, ya que también se puede representar cada línea como una lista de palabras (lo que dijo Arnauld), y lo único que esta regla agrega al desafío es la necesidad de dividir las líneas, algo que no necesariamente forma parte del desafío.
Erik el Outgolfer
2
¡Este título suena como el juego promedio de Team Fortress 2!
Tvde1

Respuestas:

10

Almádena 0.5.1 , 16 15 bytes

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

Se descomprime en esta función Wolfram Language (la final &es implícita):

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

Pruébalo en línea!

Transpose[StringSplit @ #1]: Divida cada cadena en la lista de entrada y tome las columnas (identidades de espía)

RelationGraph[Inner[Equal, ##1, Or] &, ...]: Construya el gráfico donde dos vértices comparten un borde si al menos una posición es igual (si algún agente amigo los clasifica como el mismo espía)

Length[ConnectedComponents[...]]: El número de componentes conectados es el límite superior del número posible de espías.

lirtosiast
fuente
9

JavaScript (Node.js) ,  155 150 142  141 bytes

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

Pruébalo en línea!

¿Cómo?

xmx

+---------+-------+-------+-------+-------+-------+-------+
| 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   |
+---------+-------+-------+-------+-------+-------+-------+

Comentado

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
Arnauld
fuente
Entonces ... en la práctica, ¿esto tendría un límite de identidad de 32 o 64?
Vilx-
@ Vilx: creo que podría cambiar a BigInt, aunque eso costaría bytes, por supuesto.
Neil
6

Jalea , 19 bytes

ḲiⱮ`)ZŒc€ẎyⱮ@ƒƊÐLQL

Pruébalo en línea!

Toma datos como una lista de líneas separadas por espacios (el pie de página lo explica).

Nota: ḲŒQ)PSno , no funciona.

Erik el Outgolfer
fuente
6

Python 3 , 132 162 154 139 135 bytes

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

Pruébalo en línea!

Esta es una implementación muy compacta de un algoritmo gráfico que identifica clústeres.

  1. Para cada agente, creamos un mapa de perfiles y sus alias, que es el índice más bajo de la apariencia: [map(b.index,b)for b in map(str.split,a)]. Es decir, [0,1,2,1,2]identifica tres espías, donde el primer perfil pertenece a uno, el segundo y cuarto a otro y el tercero y quinto al último. El índice del grupo también es el índice del primer perfil del grupo.

  2. Al transponer esta matriz ( [*zip(*m...)]), obtenemos una membresía de grupo para cada perfil. Esto forma un gráfico acíclico dirigido, porque los índices de grupo son un subconjunto de los índices de perfil, y todos los bordes van hacia índices más bajos o iguales. Los perfiles correspondientes al mismo espía ahora forman un clúster sin conexiones a los otros perfiles. Sin embargo, todavía tenemos rutas duplicadas, porque los índices de perfil están vinculados a índices de múltiples grupos.

  3. Con los siguientes bucles, minimizamos el gráfico en un bosque plano, donde todos los perfiles están vinculados directamente al índice más bajo de su árbol, es decir, la raíz: min(min(u)for u in r if min(w)in u)

  4. Por último, devolver el número de raíces en el bosque, es decir, índices enlazados a sí mismos: return sum(i==...).

movatica
fuente
¿Es necesaria la sangría? Han pasado años desde que usé Python, pero creo recordar que puedes hacer oneliners.
Mark Gardner
Puedes, pero no si usas bucles anidados. TIO para ti;)
movatica
5

Carbón , 49 43 bytes

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

Pruébalo en línea! El enlace es a la versión detallada del código. Posiblemente podría guardar un par de bytes utilizando un formato de entrada engorroso. Explicación:

≔⪪S θ

Ingrese la lista del primer agente.

WS«

Repita para los agentes restantes.

≔⪪ι ι

Ingrese su lista.

FLι

Pase sobre cada índice de elemento.

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

Encuentre el primer elemento en la lista de este agente con la misma identidad y actualice la lista del primer agente para mostrar que son la misma identidad.

ILΦθ⁼κ⌕θι

Cuente el número de identidades únicas restantes.

Neil
fuente
5

Jalea , 25 15 bytes

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

Pruébalo en línea!

Un enlace monádico que toma una lista de reclamos de agentes que separan el espacio y devuelve el límite superior más bajo del número de espías distintos.

Explicación

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

¡Gracias a @Arnauld y @JonathanAllan por identificar problemas con versiones anteriores, y a @JonathanAllan nuevamente por guardar un byte! Si la especificación de entrada se relajara para permitir una lista de listas, esto ahorraría un byte.

Nick Kennedy
fuente
Creo que el ordenamiento puede ser realmente innecesario, ya que los índices de los grupos Ġse ordenan y el resultado de filtro aplanado y desduplicado fƇFQ, siempre, después de una aplicación repetida, terminaría con estos en orden ordenado (por ejemplo 'a a b b c', 'a b a b c, no encontrará un eventual [3,4,1,2], aunque aparecería en el camino). Entonces ḲĠ)ẎfƇFQɗⱮQ$ÐLL, ¿ podría ser bueno para 15?
Jonathan Allan
@JonathanAllan buen lugar. He jugado un poco (y pienso en cómo funciona) y creo que tienes razón.
Nick Kennedy
4

JavaScript (Node.js) , 120 bytes

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

Pruébalo en línea!

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
tsh
fuente
3

Casco , 12 bytes

LωomΣknṁoηkw

Pruébalo en línea!

Explicación

La idea es crear una lista de todos los grupos de espías que se sabe que son la misma persona, luego fusionar progresivamente los grupos que se cruzan hasta llegar a un punto fijo. El resultado es el número de grupos restantes que no se pudieron fusionar.

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
Zgarb
fuente
3

Python 3 ,191 182 bytes

Gracias recursivo

e=enumerate
def f(a):
	r=[list(map(b.index,b))for b in map(str.split,a)]
	for z in r:
		for i,v in e(z):
			for x in(i>v)*r:x[(i,x[i])[x[i]<i]]=z[v]
	return sum(i==v for i,v in e(z))

Pruébalo en línea!

usuario24343
fuente
-9 bytes: tio.run/…
recursivo
3

Ruby , 123117 bytes

Utiliza una idea similar a la solución Python 3 de movatica, pero calcula el índice de espionaje más bajo para cada "árbol" de una manera ligeramente diferente (haciendo un seguimiento de los perfiles encontrados anteriormente, encontrando una superposición si existe y combinándolos)

-6 bytes de @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}

Pruébalo en línea!

Explicación

->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
Tinta de valor
fuente
En lugar de hacer estallar y comprimir, puedes simplemente transponer.
GB
@GB gracias por el aviso; ¡He estado usando pop-zip o shift-zip para transponer matrices para siempre! Además, su truco de uso s.split.map{|i|s.index i}es bueno, pero puede crear casos extremos dependiendo de la longitud de las entradas. Esta entrada debe devolver 3, no 2.
Value Ink
2

Python 2 , 229 221 bytes

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

Pruébalo en línea!

8 bytes thx a wilkben .

Chas Brown
fuente
Como gsolo se usa una vez, ¿no podría definirlo en línea? Olvidé un poco si eso es posible en Python, pero creo recordar que sí.
Stephen
221 Bytes
wilkben
1

Limpio , 137 bytes

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

Pruébalo en línea!

Asocia las cadenas utilizadas por los agentes con el número de línea en el que aparecen para evitar la igualdad entre los agentes, luego verifica repetidamente si alguna frase para cualquier posición se superpone y cuenta el número de conjuntos resultantes.

Οurous
fuente
0

PHP , 271 bytes

Esto no funcionará si alguna de las identidades son solo números, ya que guardo el "número de espía" con las identidades. Sin embargo, no creo que sea difícil arreglar eso.

$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)));

Pruébalo en línea!

Explicación

¡Me confundí al escribir esto, pero funciona para todos los casos de prueba!

$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

Pruébalo en línea!

Sam Dean
fuente