Quá nhiều gián điệp!


38

Bạn đang chiến đấu với một mạng lưới gián điệp rộng lớn của kẻ thù . Bạn biết rằng mỗi điệp viên có ít nhất một (đôi khi nhiều) danh tính giả mà họ muốn sử dụng. Bạn thực sự muốn biết có bao nhiêu gián điệp mà bạn thực sự phải đối phó.

May mắn thay, phản gián của bạn đại lý đang làm công việc của họ và có thể đôi khi hình dung ra khi hai sắc giả đang thực sự kiểm soát bởi các điệp viên cùng một kẻ thù.

Điều đó có nghĩa là:

  • Tuy nhiên, các đặc vụ của bạn không biết khi nào hai danh tính giả có cùng một gián điệp đằng sau họ, tuy nhiên
  • Nếu một đặc vụ cho bạn biết hai danh tính giả được kiểm soát bởi cùng một điệp viên, bạn tin rằng họ đúng.

Tin nhắn đại lý

Các đại lý gửi cho bạn các tin nhắn khó hiểu cho bạn biết danh tính nào có cùng một gián điệp đằng sau chúng. Một ví dụ:

Bạn có 2 đại lý5 danh tính giả để đối phó.

Tác nhân đầu tiên gửi cho bạn một tin nhắn:

Red Red Blue Orange Orange

Điều này có nghĩa là họ nghĩ có 3 điệp viên:

  • cái đầu tiên (Đỏ) kiểm soát danh tính 1 và 2
  • cái thứ hai (màu xanh) kiểm soát danh tính 3
  • cái thứ ba (Orange) kiểm soát danh tính 4 và 5

Tác nhân thứ hai gửi cho bạn một tin nhắn:

cat dog dog bird fly

Điều này có nghĩa là họ nghĩ có 4 điệp viên:

  • người đầu tiên (mèo) kiểm soát danh tính 1
  • cái thứ hai (chó) kiểm soát danh tính 2 và 3
  • con thứ ba (chim) điều khiển danh tính 4
  • cái thứ tư (bay) điều khiển danh tính 5

Biên dịch intel chúng ta thấy:

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

Điều này có nghĩa là có nhiều nhất 2 điệp viên .

Ghi chú

Các danh tính thuộc sở hữu của cùng một điệp viên không cần phải liên tiếp, tức là một thông điệp như:

dog cat dog

có giá trị

Ngoài ra, cùng một từ có thể được sử dụng bởi hai tác nhân khác nhau - điều đó không có nghĩa gì cả, đó chỉ là sự trùng hợp, ví dụ:

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

Nước đá được sử dụng bởi cả hai tác nhân - việc Icesử dụng bởi tác nhân thứ nhất không liên quan đến hai lần Icesử dụng của tác nhân thứ hai.

Thử thách

Tổng hợp tất cả thông tin của các đặc vụ của bạn và tìm hiểu xem có bao nhiêu gián điệp của kẻ thù thực sự. (Để chính xác hơn, hãy lấy giới hạn trên thấp nhất, với thông tin hạn chế bạn có.)

Mã ngắn nhất tính bằng byte thắng.

Thông số đầu vào và đầu ra

Đầu vào là danh sách n dòng, đại diện cho n tin nhắn từ các tác nhân. Mỗi dòng bao gồm các mã thông báo phân tách không gian k, cùng k cho tất cả các dòng. Mã thông báo là chữ và số, độ dài tùy ý. Vấn đề.

Đầu ra phải là một số duy nhất, đại diện cho số lượng gián điệp riêng biệt, dựa trên thông tin của các đại lý của bạn.

Ví dụ

ví dụ 1

Đầu vào:

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

Đầu ra:

2

Ví dụ 2

Đầu vào:

Blossom Bubbles Buttercup
Ed Edd Eddy

Đầu ra:

3

Ví dụ 3

Đầu vào:

Botswana Botswana Botswana
Left Middle Right

Đầu ra:

1

Ví dụ 4

Đầu vào:

Black White
White Black

Đầu ra:

2

Ví dụ 5

Đầu vào:

Foo Bar Foo
Foo Bar Bar

Đầu ra:

1

Ví dụ 6

Đầu vào:

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

Đầu ra:

1

Ví dụ 7

Đầu vào:

A B A C

Đầu ra:

3

Ví dụ 8

Đầu vào:

A
B
C

Đầu ra:

1

Ví dụ 9

Đầu vào:

X

Đầu ra:

1

Chúng ta có thể lấy mỗi dòng là một mảng các từ?
Arnauld

8
@HenryHenrinson Điều duy nhất khiến cho đầu vào trở nên nghiêm ngặt là thêm một đoạn ngắn khi bắt đầu mã để thay đổi định dạng đầu vào. Nó không thực sự thêm bất cứ điều gì vào thử thách
fnɛtɪk

6
Âm thanh với tôi như thế sẽ cho nhiều cơ hội hơn để chơi mã :)
Henry Henrinson

17
Các định dạng I / O nghiêm ngặt thực sự không được khuyến khích vì chúng làm mất đi cốt lõi của thách thức. Ví dụ, việc thực thi rằng đầu vào ở dạng dòng các từ được phân tách bằng dấu cách là không cần thiết, vì người ta cũng có thể biểu diễn mỗi dòng dưới dạng một danh sách các từ (những gì Arnauld đã nói) và điều duy nhất quy tắc này thêm vào thách thức là sự cần thiết phải phân chia các dòng, một cái gì đó không nhất thiết là một phần của thách thức.
Erik the Outgolfer

2
Tiêu đề này nghe giống như trò chơi Team Fortress 2 trung bình của bạn!
Tvde1

Câu trả lời:


10

Sledgehammer 0.5.1 , 16 15 byte

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

Giải nén vào chức năng Ngôn ngữ Wolfram này (cuối cùng &là ẩn):

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

Hãy thử trực tuyến!

Transpose[StringSplit @ #1]: Tách từng chuỗi trong danh sách đầu vào và lấy các cột (danh tính gián điệp)

RelationGraph[Inner[Equal, ##1, Or] &, ...]: Xây dựng biểu đồ trong đó hai đỉnh chia sẻ một cạnh nếu ít nhất một vị trí bằng nhau (nếu chúng được phân loại là cùng một gián điệp bởi một số tác nhân thân thiện)

Length[ConnectedComponents[...]]: Số lượng các thành phần được kết nối là giới hạn trên của số lượng gián điệp có thể.


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

Hãy thử trực tuyến!

Làm sao?

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

Đã bình luận

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

Vậy ... trong thực tế, điều này sẽ có giới hạn nhận dạng 32 hay 64?
Vilx-

@ Vilx- Tôi nghĩ rằng anh ấy có thể chuyển sang BigInt, mặc dù điều đó sẽ tốn byte tất nhiên.
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))

Hãy thử trực tuyến!

Đây là một triển khai rất nhỏ gọn của một cụm xác định thuật toán đồ thị.

  1. Đối với mỗi tác nhân, chúng tôi tạo một bản đồ hồ sơ và bí danh của họ, đó là chỉ số xuất hiện thấp nhất : [map(b.index,b)for b in map(str.split,a)]. Tức là [0,1,2,1,2]xác định ba điệp viên, trong đó hồ sơ đầu tiên thuộc về một, thứ hai và thứ tư cho một người khác và thứ ba và thứ năm cho người cuối cùng. Chỉ số nhóm cũng là chỉ số của hồ sơ đầu tiên trong nhóm.

  2. Bằng cách hoán vị ma trận này ( [*zip(*m...)]), chúng tôi có được tư cách thành viên nhóm cho mỗi hồ sơ. Điều này tạo thành một biểu đồ tuần hoàn, có hướng, bởi vì các chỉ mục nhóm là một tập hợp con của các chỉ mục hồ sơ và tất cả các cạnh đều hướng tới các chỉ số thấp hơn hoặc bằng nhau. Các hồ sơ tương ứng với cùng một gián điệp hiện tạo thành một cụm không có kết nối với các hồ sơ khác. Chúng tôi vẫn có các đường dẫn trùng lặp, bởi vì các chỉ mục hồ sơ được liên kết với nhiều chỉ mục nhóm.

  3. Với các vòng lặp sau, chúng tôi thu nhỏ đồ thị thành một khu rừng bằng phẳng, nơi tất cả các cấu hình được liên kết trực tiếp đến chỉ số thấp nhất trong cây của chúng, tức là gốc: min(min(u)for u in r if min(w)in u)

  4. Cuối cùng, trả về số lượng rễ trong rừng, tức là các chỉ số liên kết với chính họ : return sum(i==...).


thụt là cần thiết? nó đã có từ rất lâu kể từ khi tôi sử dụng python, nhưng tôi dường như nhớ rằng bạn có thể tạo ra oneliners.
Đánh dấu

Bạn có thể, nhưng không nếu bạn sử dụng các vòng lặp lồng nhau. TIO cho chính mình;)
Movatica

5

Than , 49 43 byte

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

Hãy thử trực tuyến! Liên kết là phiên bản dài dòng của mã. Có thể có thể lưu một vài byte bằng cách sử dụng định dạng đầu vào rườm rà. Giải trình:

≔⪪S θ

Nhập danh sách của đại lý đầu tiên.

WS«

Lặp lại cho các tác nhân còn lại.

≔⪪ι ι

Nhập danh sách của họ.

FLι

Lặp lại trên mỗi chỉ số phần tử.

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

Tìm phần tử đầu tiên trong danh sách của tác nhân này có cùng danh tính và cập nhật danh sách của tác nhân đầu tiên để cho thấy rằng chúng là cùng một danh tính.

ILΦθ⁼κ⌕θι

Đếm số lượng danh tính duy nhất còn lại.


5

Thạch , 25 15 byte

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

Hãy thử trực tuyến!

Một liên kết đơn thể lấy một danh sách các yêu cầu đại lý phân tách không gian và trả về giới hạn trên thấp nhất của số lượng gián điệp riêng biệt.

Giải trình

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

Cảm ơn @Arnauld và @Jonathan ALLan vì đã xác định các vấn đề với các phiên bản trước và một lần nữa @JonathanAllan để lưu một byte! Nếu thông số kỹ thuật đầu vào được nới lỏng để cho phép một danh sách các danh sách, điều này sẽ tiết kiệm một byte.


Tôi nghĩ rằng việc sắp xếp thực sự có thể là không cần thiết, vì các chỉ số trong các nhóm từ Ġđược sắp xếp và kết quả bộ lọc được làm phẳng, không trùng lặp fƇFQ, sẽ luôn luôn, sau khi ứng dụng lặp đi lặp lại, kết thúc với các thứ tự được sắp xếp (ví dụ: 'a a b b c', 'a b a b csẽ không tìm thấy sự kiện cuối cùng [3,4,1,2], mặc dù nó sẽ xuất hiện trên đường đi). Vì vậy, ḲĠ)ẎfƇFQɗⱮQ$ÐLLcó thể là tốt cho 15?
Jonathan Allan

@Jonathan Allan điểm tốt. Tôi đã có một chút chơi (và suy nghĩ về cách nó hoạt động) và nghĩ rằng bạn đúng.
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

Hãy thử trực tuyến!

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

Husk , 12 byte

LωomΣknṁoηkw

Hãy thử trực tuyến!

Giải trình

Ý tưởng là tạo ra một danh sách tất cả các nhóm gián điệp được biết là cùng một người, sau đó hợp nhất dần dần các nhóm giao nhau cho đến khi đạt được một điểm cố định. Đầu ra là số lượng các nhóm còn lại không thể hợp nhất.

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 byte

Sử dụng một ý tưởng tương tự như giải pháp Python 3 của Movatica nhưng tính toán chỉ số gián điệp thấp nhất cho mỗi "cây" theo một cách hơi khác nhau (bằng cách theo dõi các hồ sơ gặp phải trước đó, tìm sự trùng lặp nếu nó tồn tại và kết hợp chúng)

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

Hãy thử trực tuyến!

Giải trình

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

Thay vì popping và nén, bạn chỉ có thể chuyển đổi.
GB


@GB cảm ơn vì đã ngẩng cao đầu; Tôi đã sử dụng pop-zip hoặc shift-zip để hoán chuyển mảng mãi mãi! Ngoài ra, mẹo sử dụng của bạn s.split.map{|i|s.index i}là tốt, nhưng nó có thể tạo ra các trường hợp cạnh tùy thuộc vào độ dài của đầu vào. Đầu vào này sẽ trả về 3, không phải 2.
Giá trị 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

Hãy thử trực tuyến!

8 byte thx đến wilkben .


gchỉ được sử dụng một lần, bạn không thể định nghĩa nó là nội tuyến? Tôi hơi quên nếu điều đó có thể xảy ra với Python nhưng tôi dường như nhớ nó.
Stephen


1

Sạch , 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..]]))

Hãy thử trực tuyến!

Liên kết các chuỗi được sử dụng bởi các tác nhân với số dòng xuất hiện để ngăn sự bình đẳng giữa các tác nhân, sau đó kiểm tra liên tục nếu có bất kỳ cụm từ nào cho bất kỳ vị trí nào trùng nhau và đếm số lượng tập kết quả.


0

PHP , 271 byte

Điều này sẽ không hoạt động nếu bất kỳ danh tính nào chỉ là số khi tôi lưu trữ "số gián điệp" trong danh tính. Tôi không nghĩ rằng sẽ không khó để khắc phục điều đó.

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

Hãy thử trực tuyến!

Giải trình

Loại nhầm lẫn bản thân tôi viết này nhưng nó hoạt động cho tất cả các trường hợp thử nghiệm!

$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

Hãy thử trực tuyến!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.