洛谷 B3618 寻找团伙 (DFS,状态压缩)

寻找团伙

题目描述

世界局势风云变幻,你想办一件大事。办事自然要有人参与,你能从 n n n 个人里面挑选一部分人共襄盛举。

要办这件事,一共涉及 k k k 方面的能力,例如游说他人的能力、玩游戏的能力、睡觉的能力。每位人士都会具备某一些能力,例如机器猫就可能擅长睡觉、擅长玩游戏,而不擅长游说他人。

你的计划很宏伟,因此你希望团队拥有很全面的能力。不幸的是,如果团队中有偶数个人拥有同一类能力,那么他们就会分成两派,争执不下,导致整个团队丧失这方面的能力。相应地,如果这项能力只有奇数个人拥有,那么他们总能形成一个多数派,帮团队去做这方面的工作。

需要注意的是,团队拥有的每一项能力,对计划的成功率的贡献是不一样的。第一项能力最重要,它的权重是 2 k − 1 2^{k-1} 2k1;第二项能力的权重是 2 k − 2 2^{k-2} 2k2;依次类推。第 k k k 项能力最不重要,权重只有 1 1 1

计划的成功率得分,即是团队拥有的所有能力对应的权重之和

你希望计划成功率最大。因此,你需要选出合适的人士,来参与到你的宏图伟业中。

输入格式

第一行,两个正整数 n , k n, k n,k。分别表示供你挑选的人的数量,以及能力的种类数。
接下来 n n n 行,每行表示每个人拥有的能力。这一行首先有一个整数 c c c,表示该人士拥有多少种能力;接下来是 c c c [ 1 , k ] [1, k] [1,k] 之间的正整数,表示该人士拥有哪些能力。

输出格式

仅一行,一个整数,表示计划的成功率得分。

样例 #1

样例输入 #1

5 5
1 1
1 2
1 3
1 4
1 5

样例输出 #1

31

样例 #2

样例输入 #2

3 5
3 1 2 3
4 2 3 4 5
2 3 4

样例输出 #2

28

样例 #3

样例输入 #3

3 5
2 1 2
3 5 3 2
3 4 2 5

样例输出 #3

30

样例 #4

样例输入 #4

21 60
0 
0 
3 60 27 48
0 
1 48
2 52 14
2 4 31
0 
0 
2 28 43
2 6 31
0 
1 7
3 45 6 48
0 
1 51
0 
2 28 20
2 37 51
1 8
53 59 39 29 23 53 27 13 16 44 34 38 24 9 32 58 54 31 1 7 45 3 30 36 17 48 42 22 18 21 6 11 25 33 37 52 10 60 49 57 2 28 8 14 5 47 4 41 35 43 50 46 26 12

样例输出 #4

1152884121210322895

提示

样例解释

第一组样例,共 5 个人,每个人拥有的能力不一样。最终选择的结果是让这 5 个人都参与计划,得分 16 + 8 + 4 + 2 + 1 = 31 16+8+4+2+1 = 31 16+8+4+2+1=31

第二组样例,我们选择只让 1 1 1 参与。那么团队具有能力 1 , 2 , 3 1,2, 3 1,2,3,得分 16 + 8 + 4 = 28 16+8+4=28 16+8+4=28

第三组样例,我们让 1 , 2 , 3 1,2,3 1,2,3 参与。由于团队中有偶数个成员拥有能力 5 5 5,故团队并不拥有能力 5 5 5。奇数个成员拥有能力 2 2 2,故团队拥有能力 2 2 2。最终,团队具有能力 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4。得分 16 + 8 + 4 + 2 = 30 16+8+4+2=30 16+8+4+2=30

数据规模与约定

对于 100 % 100\% 100% 的数据,有 n ≤ 21 , k ≤ 60 n\leq 21, k\leq 60 n21,k60


如果主要到了权重都是 2 2 2 的n次方并且第四个样例的结果都达到19位了,就应该意识到这道题不能直接用二维数组存每个人有哪些点。

如果权重都是 2 2 2 的几次方,就意味着都是可以用二进制01串表示出来的,假如一个人会 1 1 1 3 3 3,那么状态表示为:101,那么这个二进制数本身表示的数是 5 5 5,即 2 2 + 2 0 = 5 2^{2} + 2^0 = 5 22+20=5,这样,本题的状态表示就和值的表示统一了。

对于二进制的状态压缩,使用unsigned long long来存,在输入时,使用或运算来存入相应数位,在枚举选中或不选某个人的时候,直接使用抑或运算,因为应题目要求,如果某个能力拥有人数为偶数就相当于为0。

CODE:

#include<bits/stdc++.h>
using namespace std;
const int N = 25;
const int K = 65;
#define ull unsigned long long

int n,k;
ull a[N];
ull res = 0;

void dfs(int u,ull sum){
    if(u == n){
        res = max(res,sum);
        return;
    }    
    
    dfs(u+1,sum^a[u]);
    
    dfs(u+1,sum);
}

int main(){
    cin >> n >> k;
    for(int i =0 ;i < n;i++){    
        int c;cin >> c;
        for(int j = 0;j < c;j++){
            int m;cin >> m;
            a[i] |= (1ull << (k - m));
        }
    }
    dfs(0,0);
    
    cout << res << endl;
    
    return 0;
}
  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种数据结构,可以有效地解决团伙问题。团伙问题指的是在一个群体中,找出其中的团伙关系。举个例子,假设有一群人,其中有些人是朋友,有些人是陌生人,现在需要找出所有的朋友团伙。这个问题可以用并查集来解决。 具体实现过程如下: 1. 初始化并查集,将每个人看作一个单独的团伙,即每个人的父节点都是自己。 2. 遍历所有的朋友关系,将每个朋友关系所在的两个人合并到同一个团伙中。合并的过程中,需要找到两个人所在团伙的根节点,将其中一个根节点的父节点指向另一个根节点。 3. 遍历所有的人,统计每个团伙的人数。具体方法是遍历每个人,找到该人所在团伙的根节点,然后统计该根节点下所有子节点的个数。 4. 输出所有团伙的人数。 下面是并查集实现团伙问题的示例代码(假设已经读入了朋友关系列表friend_list): ```python # 初始化并查集 parent = {} for i in range(len(friend_list)): parent[i] = i # 合并朋友关系所在的团伙 for f1, f2 in friend_list: root1 = find_root(parent, f1) root2 = find_root(parent, f2) if root1 != root2: parent[root1] = root2 # 统计每个团伙的人数 group_sizes = {} for i in range(len(friend_list)): root = find_root(parent, i) if root not in group_sizes: group_sizes[root] = 1 else: group_sizes[root] += 1 # 输出每个团伙的人数 for root, size in group_sizes.items(): print("团伙{}有{}个人".format(root, size)) # 找到节点的根节点 def find_root(parent, node): while parent[node] != node: node = parent[node] return node ``` 这个代码实现了一个简单的并查集,并可以用来解决团伙问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值