zoukankan      html  css  js  c++  java
  • 【Codeforces】CF367D Sereja and Sets (数学)

    题目大意

      1到n这n个正整数被分成了m个不相交的集合(集合不一定连续),现在从这m个集合中选出最少个数的集合,满足对于[1,n]中任意一个长度为d的区间都至少有一个数字出现在已选集合中。(1 <= d <= n <= 100000, 1 <= m <= 20)

    输入输出样例
    输入样例1
      3 2 2
      1 2
      2 1 3
    输出样例1
      1
    输入样例2
      5 1 1
      5 4 5 3 2 1
    输出样例2
      1
    输入样例3
      7 3 1
      4 1 3 5 7
      2 2 6
      1 4
    输出样例3
      3

    分析

      本来想贪心做的,手玩半天样例玩不出答案而且还不知道怎么写,最后选择了屈服

      考虑一个长度为d的区间如果符合条件,那么这个区间内包含的数的所属集合中任选一个,这个区间就是合法的,

      暂时称这些集合为每段区间的合法集合,这些集合可以组成一个集合组(集合的集合,姑且叫它合法集合组

      题目转化为选出数量最少集合组成一个集合组,这个集合组满足与所有的区间的合法集合组都有交集(交集的元素是若干个集合

      直接求答案不好求,我们反过来试试

      对于每个区间的合法集合组,它的补集及其子集一定是不合法的,把这些集合组都去掉,剩下的集合组就是合法的了

      然后枚举找出最小的集合组即可

      具体实现的话,从大到小枚举集合组,枚举到一个不合法的集合组就把它的子集都打上标记,枚举到合法的就更新答案

      Code

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,d,I,ans,ori[100005],mp[1050000],vis[25];
    int main()
    {
        scanf("%d%d%d",&n,&m,&d);I=(1<<m)-1;ans=m;
        for(int i=1,a,k;i<=m;i++){scanf("%d",&k);while(k--)scanf("%d",&a),ori[a]=i;}
        for(int i=1;i<=d;i++)vis[ori[i]]++;
        for(int l=1,r=d;r<=n;vis[ori[l]]--,l++,r++,vis[ori[r]]++)
        {int st=0;for(int i=1;i<=m;i++)if(vis[i])st|=1<<(i-1);mp[I-st]=1;}
        for(int st=I;st>=0;st--)
        if(mp[st]){for(int j=1;j<=m;j++)if(st&(1<<(j-1)))mp[st^(1<<(j-1))]=1;}
        else ans=min(__builtin_popcount(st),ans);
        printf("%d
    ",ans);
    }
  • 相关阅读:
    $this是什么意思-成员变量和局部变量的调用
    神经网络 ML08 c-d-e
    机器学习笔记 ML01c
    虚函数
    C++有哪几种情况只能用初始化列表,而不能用赋值?
    C++ 的 I/O
    引用
    宏定义 #define 和常量 const 的区别
    怎么设置才能让外网ip可以访问mysql数据库[转]
    大师的框架面试总结[转]
  • 原文地址:https://www.cnblogs.com/firecrazy/p/11753909.html
Copyright © 2011-2022 走看看