多多的营救行动
拼多多技术岗 7月3号笔试 第二题
题目内容
多多国的公主被邪恶势力抓走后关押在地牢中,地牢中有 nnn 片区域,分别驻守着 111 个守卫,每个守卫会监视若干个固定的区域。多多勇士要营救公主,必须打倒所有守卫,同时保证行动不被暴露。具体规则如下:
- 多多武艺高强,可以使用暗器同时打倒 111 或 222 个守卫。
- 若一个守卫所在的区域没有被其他守卫监视,打倒他不会暴露行动。
- 若两个守卫都只被彼此监视,同时打倒他们不会暴露行动。注:AAA 只被 BBB 监视,BBB 只被 AAA 监视,但 AAA 和 BBB 可同时监视其它区域。
- 被打倒的守卫不再具备监视能力。
- 两个守卫不会在同一个区域。
输入描述
第 111 行:一个整数 nnn,表示守卫数量。 接下来 nnn 行,每行描述一个守卫,格式:xxx mmm y1y1y1 y2y2y2 ... ymymym
- xxx:该守卫位置编号,从 111 开始,依次递增
- mmm:该守卫监视的区域数量
- y1y1y1 y2y2y2 ... ymymym:该守卫监视的区域编号,不会出现重复编号
约束条件
- 1≤n≤1001 \le n \le 1001≤n≤100
- 0≤m≤1000 \le m \le 1000≤m≤100
- 1≤x,y≤1001 \le x,y \le 1001≤x,y≤100
输出描述
- 如果可以击倒所有守卫而不暴露行动,输出 SUCCESSSUCCESSSUCCESS。
- 否则输出不暴露行动下最少剩余未被打倒的守卫数量。
样例1
输入
2
1 1 2
2 0
输出
SUCCESS
题解
思路
-
根据题目输入进行建图,建立
守卫 -> 监视区域的映射。 -
循环执行删除存活守卫,具体逻辑为
- 根据
第一图建图和存活守卫反向统计区域 -> 当前被谁监视的映射 - 根据第一步映射选择删除存活守卫:
- 优先选择单点可删除,如果存活守卫区域未被任何其它守卫监视,可删。
- 其次选择双点可删,找到
x 只被 y 监视, y 只被 x 监视这种组合,可同时删除x y
- 如果一轮没有可删守卫可直接结束循环
- 根据
-
第二步执行完成之后,如果所有守卫都被删除,输出
SUCCESS,否则输出对应存活守卫数量即可
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
// 每个守卫监控区域
vector<set<int>> guard(n);
for (int i = 0; i < n; i++) {
int x, m;
cin >> x >> m;
for (int j = 0; j < m; j++) {
int y;
cin >> y;
// 1-base 转换为0-base
guard[x - 1].insert(y - 1);
}
}
// 存活守卫
set<int> alive;
for (int i = 0; i < n; i++) {
alive.insert(i);
}
while (true) {
bool flag = false;
vector<set<int>> monitored(n);
// 统计当前区间被监控数量
for (auto x : alive) {
for (auto v : guard[x]) {
monitored[v].insert(x);
}
}
// 找到未被监控区域
int target = -1;
for (auto x : alive) {
if (monitored[x].empty()) {
target = x;
break;
}
}
if (target != -1) {
alive.erase(target);
flag = true;
continue;
}
// 没有找到未监控区域,尝试寻找成对打倒情况
int a, b;
a = b = -1;
for (int x : alive) {
if (monitored[x].size() != 1) {
continue;
}
// 拿到唯一监控哪一个人
int y = *monitored[x].begin();
// 也在存活列表中,并且y也只被x唯一监控
if (alive.count(y) && monitored[y].size() == 1 && *monitored[y].begin() == x) {
a = x;
b = y;
break;
}
}
if (a != -1 && b != -1) {
alive.erase(a);
alive.erase(b);
flag = true;
}
// 无法进行消除,直接结束
if (!flag) {
break;
}
}
if (alive.empty()) {
cout << "SUCCESS";
} else {
cout << alive.size();
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
// 每个守卫监控区域
List<Set<Integer>> guard = new ArrayList<>();
for (int i = 0; i < n; i++) {
guard.add(new HashSet<>());
}
for (int i = 0; i < n; i++) {
String[] parts = br.readLine().split(" ");
int x = Integer.parseInt(parts[0]) - 1;
int m = Integer.parseInt(parts[1]);
int idx = 2;
for (int j = 0; j < m; j++) {
int y = Integer.parseInt(parts[idx++]) - 1;
guard.get(x).add(y);
}
}
// 存活守卫
Set<Integer> alive = new HashSet<>();
for (int i = 0; i < n; i++) alive.add(i);
while (true) {
boolean flag = false;
List<Set<Integer>> monitored = new ArrayList<>();
for (int i = 0; i < n; i++) monitored.add(new HashSet<>());
// 统计当前区间被监控数量
for (int x : alive) {
for (int v : guard.get(x)) {
monitored.get(v).add(x);
}
}
// 找到未被监控区域
int target = -1;
for (int x : alive) {
if (monitored.get(x).isEmpty()) {
target = x;
break;
}
}
if (target != -1) {
alive.remove(target);
continue;
}
// 成对删除
int a = -1, b = -1;
for (int x : alive) {
if (monitored.get(x).size() != 1) continue;
int y = monitored.get(x).iterator().next();
if (alive.contains(y)
&& monitored.get(y).size() == 1
&& monitored.get(y).contains(x)) {
a = x;
b = y;
break;
}
}
if (a != -1) {
alive.remove(a);
alive.remove(b);
flag = true;
}
if (!flag && target == -1) break;
}
if (alive.isEmpty()) {
System.out.println("SUCCESS");
} else {
System.out.println(alive.size());
}
}
}
python
python
import sys
def solve():
data = sys.stdin.read().strip().split()
n = int(data[0])
idx = 1
# 每个守卫监控区域
guard = [set() for _ in range(n)]
for _ in range(n):
x = int(data[idx]) - 1
m = int(data[idx + 1])
idx += 2
for _ in range(m):
y = int(data[idx]) - 1
idx += 1
guard[x].add(y)
# 存活守卫
alive = set(range(n))
while True:
flag = False
# 统计当前区间被监控数量
monitored = [set() for _ in range(n)]
for x in alive:
for v in guard[x]:
monitored[v].add(x)
# 找未被监控的
target = -1
for x in alive:
if len(monitored[x]) == 0:
target = x
break
if target != -1:
alive.remove(target)
continue
# 成对删除
a = b = -1
for x in alive:
if len(monitored[x]) != 1:
continue
y = next(iter(monitored[x]))
if (y in alive and
len(monitored[y]) == 1 and
x in monitored[y]):
a, b = x, y
break
if a != -1:
alive.remove(a)
alive.remove(b)
flag = True
if not flag and target == -1:
break
print("SUCCESS" if len(alive) == 0 else len(alive))
if __name__ == "__main__":
solve()
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on("line", line => {
input.push(...line.trim().split(/\s+/));
});
rl.on("close", () => {
let idx = 0;
const n = Number(input[idx++]);
// 每个守卫监控区域
const guard = Array.from({ length: n }, () => new Set());
for (let i = 0; i < n; i++) {
const x = Number(input[idx++]) - 1;
const m = Number(input[idx++]);
for (let j = 0; j < m; j++) {
const y = Number(input[idx++]) - 1;
guard[x].add(y);
}
}
// 存活守卫
const alive = new Set();
for (let i = 0; i < n; i++) alive.add(i);
while (true) {
let flag = false;
const monitored = Array.from({ length: n }, () => new Set());
// 统计当前区间被监控数量
for (let x of alive) {
for (let v of guard[x]) {
monitored[v].add(x);
}
}
// 找未被监控
let target = -1;
for (let x of alive) {
if (monitored[x].size === 0) {
target = x;
break;
}
}
if (target !== -1) {
alive.delete(target);
continue;
}
// 成对删除
let a = -1, b = -1;
for (let x of alive) {
if (monitored[x].size !== 1) continue;
const y = [...monitored[x]][0];
if (
alive.has(y) &&
monitored[y].size === 1 &&
monitored[y].has(x)
) {
a = x;
b = y;
break;
}
}
if (a !== -1) {
alive.delete(a);
alive.delete(b);
flag = true;
}
if (!flag && target === -1) break;
}
console.log(alive.size === 0 ? "SUCCESS" : alive.size);
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
var n int
fmt.Fscan(in, &n)
// 每个守卫监控区域
guard := make([]map[int]bool, n)
for i := 0; i < n; i++ {
guard[i] = make(map[int]bool)
}
for i := 0; i < n; i++ {
var x, m int
fmt.Fscan(in, &x, &m)
x--
for j := 0; j < m; j++ {
var y int
fmt.Fscan(in, &y)
y--
guard[x][y] = true
}
}
// 存活守卫
alive := make(map[int]bool)
for i := 0; i < n; i++ {
alive[i] = true
}
for {
flag := false
// 统计当前区间被监控数量
monitored := make([]map[int]bool, n)
for i := 0; i < n; i++ {
monitored[i] = make(map[int]bool)
}
for x := range alive {
for v := range guard[x] {
if alive[v] {
monitored[v][x] = true
}
}
}
// 找未被监控
target := -1
for x := range alive {
if len(monitored[x]) == 0 {
target = x
break
}
}
if target != -1 {
delete(alive, target)
continue
}
// 成对删除
a, b := -1, -1
for x := range alive {
if len(monitored[x]) != 1 {
continue
}
var y int
for k := range monitored[x] {
y = k
}
if alive[y] && len(monitored[y]) == 1 {
for k := range monitored[y] {
if k == x {
a = x
b = y
}
}
}
if a != -1 {
break
}
}
if a != -1 {
delete(alive, a)
delete(alive, b)
flag = true
}
if !flag && target == -1 {
break
}
}
if len(alive) == 0 {
fmt.Println("SUCCESS")
} else {
fmt.Println(len(alive))
}
}