1 条题解

  • 0
    @ 2026-2-18 15:59:26
    #include<bits/stdc++.h>
    using namespace std;
    
    // [1] 全局常量:最大团伙数量限制,适配题目n≤1000的范围
    const int MAXN = 1005;
    
    // [2] 全局数组:并查集父节点数组,存储每个团伙的父节点编号
    int fa[MAXN];
    // [3] 全局数组:并查集连通块大小数组,仅根节点的sz值有效,记录对应连通块的团伙数量
    int sz[MAXN];
    // [4] 全局数组:邻接表,存储每个团伙的直接联系邻居
    vector<int> g[MAXN];
    
    // 函数声明
    int find(int x);
    void unite(int x, int y);
    bool check(int k, int n);
    
    // [5] 主函数:程序运行入口
    int main()
    {
        int n;
        cin >> n; // [6] 读入犯罪团伙的总数量n
        
        // [7] 循环读入每个团伙的邻居信息,构建邻接表
        for(int i = 1; i <= n; i++)
        {
            int cnt;
            cin >> cnt; // [8] 读入当前团伙的邻居数量
            // [9] 循环读入所有邻居编号,加入邻接表
            for(int j = 0; j < cnt; j++)
            {
                int neighbor;
                cin >> neighbor;
                g[i].push_back(neighbor);
            }
        }
        
        // [10] 二分查找最小的k:左边界0(不打击任何团伙),右边界n(打击所有团伙)
        int l = 0, r = n, ans = n;
        while(l <= r)
        {
            int mid = (l + r) / 2; // [11] 计算当前二分的中间值mid,即尝试打击1~mid的团伙
            // [12] 调用check函数判断mid是否可行,跳转至该函数完成注释
            if(check(mid, n))
            {
                // [25] 回到主函数,mid可行,尝试寻找更小的k
                ans = mid; // 更新答案为当前mid
                r = mid - 1; // 右边界左移,尝试更小的k
            }
            else
            {
                // [26] mid不可行,需要打击更多团伙,左边界右移
                l = mid + 1;
            }
        }
        
        cout << ans << endl; // [27] 输出最小的k值
        return 0; // [28] 主函数正常结束
    }
    
    // [13] 跳转至find函数:带路径压缩,查找节点x所在连通块的根节点
    int find(int x)
    {
        // [14] 函数参数x:待查找根节点的团伙编号
        if(fa[x] != x)
        {
            fa[x] = find(fa[x]); // [15] 路径压缩:直接将当前节点指向根节点,优化后续查询效率
        }
        return fa[x]; // [16] 返回找到的根节点
    }
    // [17] find函数注释完成,返回unite函数继续注释
    
    // [18] 跳转至unite函数:按大小合并x和y所在的两个连通块
    void unite(int x, int y)
    {
        // [19] 函数参数x、y:待合并的两个团伙编号
        x = find(x); // [20] 查找x的根节点
        y = find(y); // [20] 查找y的根节点
        
        if(x == y) return; // [21] 两团伙已属于同一连通块,无需重复合并
        
        // [22] 按大小合并:将小的连通块合并到大的连通块中,保持树结构平衡
        if(sz[x] < sz[y]) swap(x, y);
        fa[y] = x;
        sz[x] += sz[y]; // [23] 更新合并后连通块的大小
    }
    // [24] unite函数注释完成,返回check函数继续注释
    
    // [12] 跳转至check函数:检查打击1~k的团伙后,剩余团伙的最大连通块是否≤n/2
    bool check(int k, int n)
    {
        // [13] 函数参数k:打击的团伙编号上限;n:团伙总数量
        // [14] 循环初始化并查集
        for(int i = 1; i <= n; i++)
        {
            fa[i] = i; // [15] 每个团伙初始独立为一个连通块,父节点设为自身
            sz[i] = 1; // [16] 每个连通块的初始大小为1
        }
        
        // [17] 遍历所有未被打击的团伙,合并符合条件的边
        for(int i = k+1; i <= n; i++)
        {
            // [18] 遍历当前团伙的所有邻居
            for(int j : g[i])
            {
                if(j > k) // [19] 邻居也未被打击,合并两个团伙
                {
                    // [20] 调用unite函数合并两个团伙,跳转至该函数完成注释
                    unite(i, j);
                }
            }
        }
        
        // [24] 回到check函数,合并完成,统计最大连通块大小
        int max_sz = 0; // [25] 记录剩余团伙的最大连通块大小
        // [26] 遍历所有未被打击的团伙,统计最大连通块
        for(int i = k+1; i <= n; i++)
        {
            if(find(i) == i) // [27] 当前节点是连通块的根节点
            {
                max_sz = max(max_sz, sz[i]); // 更新最大连通块大小
            }
        }
        
        return max_sz <= n / 2; // [28] 返回检查结果:最大连通块是否符合要求
    }
    // [29] check函数注释完成,返回主函数继续注释
    

    信息

    ID
    1359
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者