C - Sleep Buddies Gym - 101063C(状态压缩)

It is nighttime in the Earth Colony on Mars and everyone is getting ready to sleep. It is common to sleep in pairs, so that if anything were to happen during the night people could aid each other.

To decide who is a suitable candidate to sleep with whom, the leaders of GEMA asked everyone to answer a questionnaire about attributes they desire their partner to have from a list of M possible items.

To choose the pairs, GEMA uses the Jaccard index between the desired attributes of both persons. The Jaccard index for two sets A and B is defined as 这里写图片描述, that is, the size of the intersection between A and B divided by the size of their union. They allow a pair to be formed if the Jaccard index of the two attribute sets for the pair is at least K.

Thanks to GEMA, there are too many people living on Mars these days. Help the high commanders decide how many allowed pairs there are out of the N people living there.

Input
The input begins with two integers, N and M (1 ≤ N ≤ 105, 1 ≤ M ≤ 10), the number of people on Mars and the number of possible attributes on the questionnaire.

Then follow N lines, each beginning with an integer Q (1 ≤ Q ≤ M), the number of desired attributes on the list of the i-th person. Then follow Q integers q (1 ≤ q ≤ M), encoding these attributes. There numbers are all different.

The last line of input contains a real number K (0 ≤ K ≤ 1), the minimum required Jaccard index for a pair.

Output
Output the number of pairs that are allowed.

Example
Input
2 5
2 1 3
5 3 1 5 4 2
0.66489
Output
0
Input
3 1
1 1
1 1
1 1
0.85809
Output
3
题意:就是给出多个集合,每个集合里包含Q个数字,求每两个集合之间的参数J大于等于k的集合有多少对。这个题直接使用for循环会超时,这里考虑状态压缩。将范围压缩到2^10以内。思路为:一个集合压缩成一个数,转化成2^x-1(为了控制在2^10以内所以是x-1),这样加完了之后转化成二进制的数就会出现集合里有哪几个数就在哪几个数位上是1,比如集合里有1 3 5则压缩完的二进制数就是2^0+2^2+2^4=10101(0,2,4位上是1)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int book[2014], d[2014];
int main()
{
    int n, m, q, i, j, x, y, t1, t2;
    double p, pt;
    while(~scanf("%d %d", &n, &m))
    {
        memset(book, 0, sizeof(book));
        while(n--)
        {
            scanf("%d", &q);
            y = 0;
            while(q--)
            {
                scanf("%d", &x);
                y += (1<<(x-1));//一个集合压缩成一个数,转化成2^x-1(为了控制在2^10以内所以是x-1),这样加完了之后转化成二进制的数就会出现集合里有哪几个数就在哪几个数位上是1,比如集合里有1 3 5则压缩完的二进制数就是2^0+2^2+2^4=10101(0,2,4位上是1)
            }
            book[y]++;//记录相同的集合有几个
        }
        scanf("%lf", &p);
        memset(d, 0, sizeof(d));
        for(i = 0; i < 1024; i++)//记录每一个二进制的i里有多少个1,即这个集合里有多少个数
        {
            x = i;
            while(x)
            {
                if(x & 1) d[i]++;//每次记录的都是最后一位是否为1
                x >>= 1;//每记录完一位就向右移,前一位覆盖掉最后一位,这样每次都看最后一位是否是1
            }
        }
        LL cnt = 0;
        for(i = 0; i < 1024; i++)
        {
            for(j = i; j < 1024; j++)
            {
                if(book[i] == 0 || book[j] == 0) continue;
                t1 = i & j;//(i和j有哪些数位上同为1则证明两个集合里都有这个数)
                t2 = i | j;
                pt = (double)d[t1]/(double)d[t2];//相当于共同的数的个数除以所有数的个数(交集除以并集)
                if(pt < p) continue;
                else
                {
                    if(i == j) cnt += (LL)book[i]*(LL)(book[i]-1)>>1;//比如样例2 i和j都是1的情况,
                    else cnt += (LL)book[i]*(LL)book[j];//算出两两之间到底多少对符合pt>p
                }
            }
        }
        printf("%lld\n", cnt);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值