助手招募
题目分析
一位大法师需要从候选人中招募三名不同专精的助手:一名"巨龙血脉"(类型1)、一名"精灵之森"(类型2)、一名"深海智者"(类型3)。每位候选人拥有4个魔法符文印记(0表示无印记)。选出的三名助手的所有非零符文印记必须互不相同,以避免力量冲突。输出所有合法的组合方案,按类型1的ID升序、类型2的ID升序、类型3的ID升序排列。若无合法方案则输出 -1。
思路
分组枚举 + 集合判交
核心思路非常直接:
- 按专精分组:将所有候选人按类型分为三组。
- 三重枚举:从三组中各选一人,检查他们的符文印记是否两两无交集。
- 剪枝优化:在枚举类型1和类型2的组合时,先判断这两人的印记是否有冲突。若有冲突则跳过内层循环,避免无效枚举。
判断两组印记是否冲突,使用集合的交集操作即可。将每个候选人的非零印记存为集合,两个集合交集为空表示无冲突。
以样例 1 为例验证: 候选人 1(类型1,印记{1,2,3,4})和候选人 2(类型2,印记{5,6,7,8})无交集,再与候选人 3(类型3,印记{9,10,11,12})也无交集,因此 (1,2,3) 是合法方案。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
vector<vector<pair<int, set<int>>>> group(3);
for (int i = 0; i < n; i++) {
int id, type;
scanf("%d%d", &id, &type);
set<int> marks;
for (int j = 0; j < 4; j++) {
int m;
scanf("%d", &m);
if (m != 0) marks.insert(m);
}
group[type - 1].push_back({id, marks});
}
for (int i = 0; i < 3; i++)
sort(group[i].begin(), group[i].end());
vector<tuple<int,int,int>> ans;
for (auto& [id1, s1] : group[0]) {
for (auto& [id2, s2] : group[1]) {
bool ok12 = true;
for (int v : s1)
if (s2.count(v)) { ok12 = false; break; }
if (!ok12) continue;
set<int> merged(s1);
merged.insert(s2.begin(), s2.end());
for (auto& [id3, s3] : group[2]) {
bool ok = true;
for (int v : s3)
if (merged.count(v)) { ok = false; break; }
if (ok) ans.push_back({id1, id2, id3});
}
}
}
if (ans.empty()) {
printf("-1\n");
} else {
sort(ans.begin(), ans.end());
for (auto& [a, b, c] : ans)
printf("%d %d %d\n", a, b, c);
}
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
in.nextToken(); int n = (int) in.nval;
List<int[]>[] group = new List[3];
for (int i = 0; i < 3; i++) group[i] = new ArrayList<>();
for (int i = 0; i < n; i++) {
in.nextToken(); int id = (int) in.nval;
in.nextToken(); int type = (int) in.nval;
int[] entry = new int[5];
entry[0] = id;
for (int j = 1; j <= 4; j++) {
in.nextToken(); entry[j] = (int) in.nval;
}
group[type - 1].add(entry);
}
for (int i = 0; i < 3; i++)
group[i].sort((a, b) -> a[0] - b[0]);
List<int[]> ans = new ArrayList<>();
for (int[] g1 : group[0]) {
Set<Integer> s1 = new HashSet<>();
for (int j = 1; j <= 4; j++) if (g1[j] != 0) s1.add(g1[j]);
for (int[] g2 : group[1]) {
Set<Integer> s2 = new HashSet<>();
for (int j = 1; j <= 4; j++) if (g2[j] != 0) s2.add(g2[j]);
boolean ok12 = true;
for (int v : s2)
if (s1.contains(v)) { ok12 = false; break; }
if (!ok12) continue;
Set<Integer> merged = new HashSet<>(s1);
merged.addAll(s2);
for (int[] g3 : group[2]) {
boolean ok = true;
for (int j = 1; j <= 4; j++)
if (g3[j] != 0 && merged.contains(g3[j])) { ok = false; break; }
if (ok) ans.add(new int[]{g1[0], g2[0], g3[0]});
}
}
}
StringBuilder sb = new StringBuilder();
if (ans.isEmpty()) {
sb.append("-1\n");
} else {
ans.sort((a, b) -> a[0] != b[0] ? a[0] - b[0] : (a[1] != b[1] ? a[1] - b[1] : a[2] - b[2]));
for (int[] t : ans)
sb.append(t[0]).append(' ').append(t[1]).append(' ').append(t[2]).append('\n');
}
System.out.print(sb);
}
}
import sys
input = sys.stdin.readline
def main():
n = int(input())
group = [[] for _ in range(3)]
for _ in range(n):
parts = list(map(int, input().split()))
cid, ctype = parts[0], parts[1]
marks = frozenset(m for m in parts[2:6] if m != 0)
group[ctype - 1].append((cid, marks))
for i in range(3):
group[i].sort()
ans = []
for id1, s1 in group[0]:
for id2, s2 in group[1]:
if s1 & s2:
continue
merged = s1 | s2
for id3, s3 in group[2]:
if not (merged & s3):
ans.append((id1, id2, id3))
if not ans:
print(-1)
else:
ans.sort()
sys.stdout.write('\n'.join(f"{a} {b} {c}" for a, b, c in ans) + '\n')
main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
const n = parseInt(lines[0]);
const group = [[], [], []];
for (let i = 1; i <= n; i++) {
const parts = lines[i].split(' ').map(Number);
const id = parts[0], type = parts[1];
const marks = new Set();
for (let j = 2; j < 6; j++)
if (parts[j] !== 0) marks.add(parts[j]);
group[type - 1].push([id, marks]);
}
for (let i = 0; i < 3; i++)
group[i].sort((a, b) => a[0] - b[0]);
const ans = [];
for (const [id1, s1] of group[0]) {
for (const [id2, s2] of group[1]) {
let ok12 = true;
for (const v of s1)
if (s2.has(v)) { ok12 = false; break; }
if (!ok12) continue;
const merged = new Set([...s1, ...s2]);
for (const [id3, s3] of group[2]) {
let ok = true;
for (const v of s3)
if (merged.has(v)) { ok = false; break; }
if (ok) ans.push([id1, id2, id3]);
}
}
}
if (ans.length === 0) {
console.log(-1);
} else {
ans.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
console.log(ans.map(t => t.join(' ')).join('\n'));
}
});
复杂度分析
设三组候选人的数量分别为 ,每人最多4个印记。
- 时间复杂度:
,三重循环枚举所有组合,每次判交为
(印记数量为常数4)。
- 空间复杂度:
,存储所有候选人信息及结果数组。

京公网安备 11010502036488号