间谍太多了!


38

您正在与庞大的敌人间谍网络作战。您知道每个间谍都有至少一个(有时是多个)他们喜欢使用的伪造身份。您真的想知道您实际上正在处理多少个间谍。

幸运的是,您的反情报特工正在做他们的工作,有时可以弄清楚何时两个假身份实际上是由同一个敌方间谍控制的。

也就是说:

  • 您的代理人并不总是知道两个伪造的身份背后何时有相同的间谍。
  • 如果代理告诉您两个假身份是由同一间谍控制的,则您相信它们是正确的。

代理讯息

代理会向您发送隐秘消息,告诉您哪些身份背后隐藏着相同的间谍。一个例子:

您需要处理2个代理5个伪造身份

第一代理向您发送一条消息:

Red Red Blue Orange Orange

这意味着他们认为有3个间谍:

  • 第一个(红色)控制标识1和2
  • 第二个(蓝色)控制标识3
  • 第三个(橙色)控制标识4和5

第二个代理向您发送一条消息:

cat dog dog bird fly

这意味着他们认为有4个间谍:

  • 第一个(猫)控制身份1
  • 第二个(狗)控制身份2和3
  • 第三个人(鸟)控制身份4
  • 第四个(飞行)控制身份5

编译intel我们看到:

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个以空格分隔的标记,所有行都相同。令牌是字母数字,任意长度。案件很重要。

输出应该是单个数字,代表基于代理程序的intel的不同间谍的数量。

例子

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

例子八

输入:

A
B
C

输出:

1

例子9

输入:

X

输出:

1

我们可以将每一行看作一个单词数组吗?
Arnauld

8
@HenryHenrinson使输入严格的唯一方法是在代码的开头添加简短的内容以更改输入格式。挑战本身并没有增加任何东西
fəˈnɛtɪk

6
在我看来,这将给高尔夫代码带来更多的机会:)
亨利·亨林森

17
不鼓励使用严格的I / O格式,因为它们会降低挑战的核心。例如,不必强制输入采用以空格分隔的单词的行的形式,因为一个人也可以将每一行表示为单词的列表(这就是Arnauld所说的),这是唯一给规则增加了挑战的事情是必须分界线的,这不一定是挑战的一部分。
暴民埃里克

2
这个标题听起来像是您的《军团要塞2》平均游戏!
Tvde1

Answers:


10

大锤0.5.116 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

在线尝试!

怎么样?

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

已评论

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

Python 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。
Mark Gardner,

可以,但如果使用嵌套的for循环则可以。自己的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

红宝石123个 117字节

使用与movatica的Python 3解决方案类似的想法,但以略有不同的方式计算每个“树”的最低间谍索引(通过跟踪先前遇到的配置文件,查找是否存在重叠并将它们组合在一起)

@GB中的-6个字节。

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


@GB感谢您的注意;我一直在使用pop-zip或shift-zip来永久转置数组!同样,您的使用技巧s.split.map{|i|s.index i}很好,但是根据输入的长度,它可能会创建边缘情况。这个输入应该返回3,不2
价值油墨

2

Python 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

在线尝试!

wilkben发送 8个字节。


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

在线尝试!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.