1 条题解

  • 0
    @ 2025-8-24 21:13:58

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 阮行止
    算法+网络安全研究者,致力于推动 OI 教育的进步

    搬运于2025-08-24 21:13:57,当前版本为作者最后更新于2022-03-13 23:16:59,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    简化题意:给定一个整数集合,寻找子集使异或和最大。

    本题 n21n\leq 21,可以暴力枚举子集。k60k\leq 60,可以采用 unsigned long long 来存储二进制位(不必开 bool 数组)。

    细节:

    • 读入每个人的能力时,应该用 p[i] |= (1ULL << (k - x)) 来标记 ii 人士拥有 xx 能力。需要注意这个 ULL,如果不加,编译器将其视为 int 型的 1,程序会 WA。

    枚举子集有多种方式。我们下面的代码采用了递归枚举,但也可以使用二进制技巧枚举子集。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef unsigned long long ull;
    ull p[30];
    int n, k;
    
    void input() {
        cin >> n >> k;
    
        for(int i=0; i<n; i++) {
            int c, x;
            cin >> c;
    
            while(c--) {
                cin >> x;
                p[i] |= (1ULL << (k - x));   // 人士 i 拥有 x 能力,修改对应 bit
            }
        }
    }
    
    int choice[30];
    ull ans;
    
    void dfs(int pos) {
        if(pos == n) {
            ull res = 0;
            for(int i=0; i<n; i++)
                if(choice[i])
                    res ^= p[i];
            
            ans = max(res, ans);
            return;
        }
    
        choice[pos] = 0;
        dfs(pos + 1);
        choice[pos] = 1;
        dfs(pos + 1);
    }
    
    void work() {
        dfs(0);
        cout << ans << endl;
    }
    
    int main() {
        input();
        work();
    
        return 0;
    }
    

    上述代码的复杂度为 O(2nk)\mathcal{O}(2^n \cdot k),其中 2n2^n 为枚举子集的复杂度。

    需要指出,本题可以采用线性基 + 贪心来做。线性基能以 O(nk)\mathcal{O}(nk) 的复杂度完成任务,在此仅给出 Python 代码,有兴趣的同学可以去学习。

    import numpy as np
    from functools import reduce
    
    n, k = map(int, input().split())
    s = np.zeros([k, k], dtype=int)
    
    for x in range(n):
        r = list(map(int, input().split()))[1:]
        p = np.array([1 if i+1 in r else 0 for i in range(k)])
        
        for pos in range(k):
            if p[pos] == 1:
                if s[pos, pos] == 1:
                    p ^= s[pos]
                else:
                    s[pos] = p
                    break
    
    res = np.zeros([k], dtype=int)
    for pos in range(k):
        if res[pos] == 0 and s[pos, pos] == 1:  # 贪心
            res ^= s[pos]
    
    print(reduce(lambda x,y: x*2+y, res))    # 二进制转整数输出
    
    • 1

    信息

    ID
    7547
    时间
    1000ms
    内存
    128MiB
    难度
    2
    标签
    递交数
    0
    已通过
    0
    上传者