Troppe spie!


38

Stai combattendo una vasta rete di spie nemiche . Sai che ogni spia ha almeno un'identità (a volte multipla) falsa che gli piace usare. Ti piacerebbe davvero sapere quante spie hai effettivamente a che fare.

Fortunatamente, i tuoi agenti di controspionaggio stanno facendo il loro lavoro e talvolta riescono a capire quando due false identità sono effettivamente controllate dalla stessa spia nemica.

Vale a dire:

  • Tuttavia, i tuoi agenti non sanno sempre quando due falsi identi hanno la stessa spia dietro di loro
  • Se un agente ti dice che due identità false sono controllate dalla stessa spia, ti fidi che hanno ragione.

Messaggi dell'agente

Gli agenti ti inviano messaggi criptici che ti dicono quali identità hanno la stessa spia dietro di loro. Un esempio:

Hai 2 agenti e 5 identità false da affrontare.

Il primo agente ti invia un messaggio:

Red Red Blue Orange Orange

Questo significa che pensano che ci siano 3 spie:

  • il primo (rosso) controlla le identità 1 e 2
  • il secondo (blu) controlla l'identità 3
  • il terzo (arancione) controlla le identità 4 e 5

Il secondo agente ti invia un messaggio:

cat dog dog bird fly

Questo significa che pensano che ci siano 4 spie:

  • il primo (cat) controlla l'identità 1
  • il secondo (cane) controlla le identità 2 e 3
  • il terzo (uccello) controlla l'identità 4
  • il quarto (vola) controlla l'identità 5

Compilando le informazioni che vediamo:

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

Ciò significa che ci sono al massimo 2 spie .

Gli appunti

Le identità possedute dalla stessa spia non devono essere consecutive, cioè un messaggio come:

dog cat dog

è valido.

Inoltre, la stessa parola potrebbe essere utilizzata da due agenti diversi: ciò non significa nulla, è solo una coincidenza, ad esempio:

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

Il ghiaccio è usato da entrambi gli agenti: quello Iceusato dal primo agente non è correlato alle due occorrenze Iceusate dal secondo agente.

Sfida

Compila le informazioni di tutti i tuoi agenti e scopri quante spie nemiche ci sono davvero. (Per essere più precisi, ottieni il limite superiore più basso, date le informazioni limitate che hai.)

Vince il codice più breve in byte.

Spec. Input e output

L'input è un elenco di n righe, che rappresentano n messaggi degli agenti. Ogni linea è composta da k token separati da spazio, lo stesso k per tutte le linee. I token sono alfanumerici, lunghezza arbitraria. Caso importante.

L'output dovrebbe essere un singolo numero, che rappresenta il numero di spie distinte, in base alle informazioni dei tuoi agenti.

Esempi

Esempio 1

Ingresso:

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

Produzione:

2

Esempio 2

Ingresso:

Blossom Bubbles Buttercup
Ed Edd Eddy

Produzione:

3

Esempio 3

Ingresso:

Botswana Botswana Botswana
Left Middle Right

Produzione:

1

Esempio 4

Ingresso:

Black White
White Black

Produzione:

2

Esempio 5

Ingresso:

Foo Bar Foo
Foo Bar Bar

Produzione:

1

Esempio 6

Ingresso:

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

Produzione:

1

Esempio 7

Ingresso:

A B A C

Produzione:

3

Esempio 8

Ingresso:

A
B
C

Produzione:

1

Esempio 9

Ingresso:

X

Produzione:

1

Possiamo prendere ogni riga come una matrice di parole?
Arnauld,

8
@HenryHenrinson L'unica cosa che rende rigoroso l'input è aggiungere un breve errore all'inizio del codice per cambiare il formato di input. In realtà non aggiunge nulla alla sfida stessa
fəˈnɛtɪk

6
Mi sembra che questo darà maggiori opportunità di giocare a golf con il codice :)
Henry Henrinson,

17
I formati I / O rigorosi sono davvero scoraggiati poiché riducono il nocciolo della sfida. Ad esempio, imporre che l'input sia in forma di linee di parole separate da spazio non è necessario, dal momento che si può anche rappresentare ogni riga come un elenco di parole (cosa ha detto Arnauld), e l'unica cosa che questa regola aggiunge alla sfida è la necessità di dividere le linee, qualcosa che non è necessariamente una parte della sfida.
Erik the Outgolfer,

2
Questo titolo sembra il tuo gioco Team Fortress 2 medio!
Tvde1

Risposte:


10

Mazza 0.5.1 , 16 15 byte

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

Si decomprime in questa funzione del linguaggio Wolfram (il finale &è implicito):

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

Provalo online!

Transpose[StringSplit @ #1]: Dividi ogni stringa nell'elenco di input e prendi le colonne (identità spia)

RelationGraph[Inner[Equal, ##1, Or] &, ...]: Costruisci il grafico in cui due vertici condividono un bordo se almeno una posizione è uguale (se sono classificati come la stessa spia da un agente amico)

Length[ConnectedComponents[...]]: Il numero di componenti collegati è il limite superiore del possibile numero di spie.


9

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

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

Provalo online!

Come?

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

Commentate

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

Quindi ... in pratica, questo avrebbe un limite di identità di 32 o 64?
Vilx-

@ Vilx- Penso che potrebbe passare a BigInt, anche se ovviamente costerebbe byte.
Neil,


6

Python 3 , 132 162 154 139 135 byte

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

Provalo online!

Questa è un'implementazione molto compatta di un algoritmo grafico che identifica i cluster.

  1. Per ogni agente, creiamo una mappa dei profili e le loro alias, che è il più basso indice di apparizione: [map(b.index,b)for b in map(str.split,a)]. Cioè [0,1,2,1,2]identifica tre spie, in cui il primo profilo appartiene a uno, il secondo e il quarto a un altro e il terzo e il quinto all'ultimo. L'indice di gruppo è anche l'indice del primo profilo nel gruppo.

  2. Trasponendo questa matrice ( [*zip(*m...)]), otteniamo un'appartenenza al gruppo per ciascun profilo. Questo forma un grafico aciclico diretto, poiché gli indici di gruppo sono un sottoinsieme degli indici di profilo e tutti i bordi vanno verso indici inferiori o uguali. I profili corrispondenti alla stessa spia ora formano un cluster senza connessioni agli altri profili. Tuttavia, abbiamo ancora percorsi duplicati, perché gli indici dei profili sono collegati a più indici di gruppi.

  3. Con i seguenti loop, minimizziamo il grafico in una foresta piatta, dove tutti i profili sono collegati direttamente all'indice più basso nella loro struttura, ovvero la radice: min(min(u)for u in r if min(w)in u)

  4. Infine, restituire il numero di radici nella foresta, cioè indici legati a se stessi: return sum(i==...).


è necessaria la rientranza? sono passati secoli da quando ho usato Python, ma mi sembra di ricordare che puoi creare oneliners.
Mark Gardner,

Puoi, ma non se usi nidificato per i loop. TIO per te;)
movatica

5

Carbone di legna , 49 43 byte

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

Provalo online! Il collegamento è alla versione dettagliata del codice. Potrebbe forse salvare un paio di byte utilizzando un formato di input ingombrante. Spiegazione:

≔⪪S θ

Immettere l'elenco del primo agente.

WS«

Ripetere l'operazione per gli altri agenti.

≔⪪ι ι

Inserisci il loro elenco.

FLι

Scorri su ogni indice di elemento.

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

Trova il primo elemento nell'elenco di questo agente con la stessa identità e aggiorna l'elenco del primo agente per mostrare che sono la stessa identità.

ILΦθ⁼κ⌕θι

Conta il numero di identità univoche rimanenti.


5

Gelatina , 25 15 byte

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

Provalo online!

Un collegamento monadico che prende un elenco di rivendicazioni dell'agente che separa lo spazio e restituisce il limite superiore più basso del numero di spie distinte.

Spiegazione

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

Grazie a @Arnauld e @JonathanAllan per l'identificazione dei problemi con le versioni precedenti e a @JonathanAllan di nuovo per aver salvato un byte! Se le specifiche di input fossero rilassate per consentire un elenco di elenchi, ciò salverebbe un byte.


Penso che l'ordinamento potrebbe in realtà non essere necessario, poiché gli indici nei gruppi da Ġvengono ordinati e il risultato del filtro appiattito e de-duplicato fƇFQ, finirebbe sempre, dopo un'applicazione ripetuta, con questi in ordine ordinato (ad es. 'a a b b c', 'a b a b cNon troverà un eventuale [3,4,1,2], anche se sembrerebbe lungo la strada). Quindi ḲĠ)ẎfƇFQɗⱮQ$ÐLLpotrebbe essere buono per 15?
Jonathan Allan,

@JonathanAllan buon posto. Ho giocato un po '(e penso a come funziona) e penso che tu abbia ragione.
Nick Kennedy,

4

JavaScript (Node.js) , 120 byte

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

Provalo online!

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

Buccia , 12 byte

LωomΣknṁoηkw

Provalo online!

Spiegazione

L'idea è quella di creare un elenco di tutti i gruppi di spie che sono noti per essere la stessa persona, quindi unire progressivamente gruppi intersecanti fino a raggiungere un punto fisso. L'output è il numero di gruppi rimanenti che non è stato possibile unire.

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

Rubino , 123 117 byte

Utilizza un'idea simile alla soluzione Python 3 di movatica ma calcola l'indice spia più basso per ogni "albero" in un modo leggermente diverso (tenendo traccia dei precedenti profili rilevati, trovando una sovrapposizione se esiste e combinandoli)

-6 byte da @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}

Provalo online!

Spiegazione

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

Invece di schioccare e zippare, puoi semplicemente trasporre.
GB


@GB grazie per il testa a testa; Ho usato pop-zip o shift-zip per trasporre array per sempre! Inoltre, il tuo trucco s.split.map{|i|s.index i}è piacevole, ma può creare casi limite a seconda della lunghezza degli input. Questo input dovrebbe restituire 3, non 2.
Value Ink

2

Python 2 , 229 221 byte

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

Provalo online!

8 byte grazie a Wilben .


Dal momento che gviene utilizzato solo una volta, non è possibile definirlo in linea? In un certo senso dimentico se è possibile in Python, ma mi sembra di ricordarlo.
Stephen,


1

Pulito , 137 byte

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

Provalo online!

Associa le stringhe utilizzate dagli agenti al numero di riga in cui compaiono per impedire l'uguaglianza tra gli agenti, quindi controlla ripetutamente se qualche frase per qualsiasi posizione si sovrappone e conta il numero di serie risultanti.


0

PHP , 271 byte

Questo non funzionerà se una qualsiasi delle identità sono solo numeri mentre memorizzo il "numero spia" con le identità. Non penso che non sarebbe difficile risolverlo però.

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

Provalo online!

Spiegazione

In un certo senso mi sono confuso mentre scrivevo questo, ma funziona per tutti i casi di test!

$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

Provalo online!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.