助手招募

题目分析

一位大法师需要从候选人中招募三名不同专精的助手:一名"巨龙血脉"(类型1)、一名"精灵之森"(类型2)、一名"深海智者"(类型3)。每位候选人拥有4个魔法符文印记(0表示无印记)。选出的三名助手的所有非零符文印记必须互不相同,以避免力量冲突。输出所有合法的组合方案,按类型1的ID升序、类型2的ID升序、类型3的ID升序排列。若无合法方案则输出 -1。

思路

分组枚举 + 集合判交

核心思路非常直接:

  1. 按专精分组:将所有候选人按类型分为三组。
  2. 三重枚举:从三组中各选一人,检查他们的符文印记是否两两无交集。
  3. 剪枝优化:在枚举类型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)。
  • 空间复杂度,存储所有候选人信息及结果数组。