TJUOJ4168解题报告【覆盖集合,想法题】

题目地址:

  http://acm.tju.edu.cn/toj/showp4168.html

题目概述:

  给出一个集合以及一个m,集合中一开始有n个数,要求加入最少的数使得这个集合的各个子集的和能够覆盖1~m。

  例如集合A={1,3},m=6,这时只需要将2加入A中,A的所有子集为[1], [2], [3], [1,2], [1,3], [2,3], [1,2,3],他们的和分别是1,2,3,3,4,5,6,能够覆盖1~6这个区间,所以答案为1。

大致思路:

  一开始想到二进制位上去了,觉得每个数的二进制位都有个贡献之类的,结果还是too naive啊,跟正解差的太多。

  首先对子集A的元素排个序,然后假设当前子集中最大的数为a[i-1],能够覆盖的范围是1~mx,那么如果a[i]>mx+1的话,必须要插入mx+1这个数才能使覆盖的范围仍旧连续,而插入mx+1,会使得覆盖的范围变成2*mx+1,此时再比较a[i]与当前的mx的大小,直到a[i]<=mx+1时加入a[i]会使得覆盖的范围变成mx+a[i],一直这么考虑下去就ok。

复杂度分析:

  首先对于数m来说,最多只需要log2(m)个数就可以满足题意了,那么插入的复杂度应该就是O(n+logm),之前还需要排序,就是O(nlogn),总的的复杂度即O(nlogn)了。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <ctime>
#include <map>
#include <assert.h>
#include <stack>
#include <set>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

#define sacnf scanf
#define scnaf scanf
#define maxn 100010
#define maxm 20010
#define inf 1061109567
#define INF 0x3f3f3f3f
#define Eps 0.000001
const double PI=acos(-1.0);
#define mod 1000000007
#define MAXNUM 10000
#define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++)
#define mes(a,b) memset((a),(b),sizeof(a))
typedef long long ll;
typedef unsigned long long ulld;
void Swap(int &a,int &b) {int t=a;a=b;b=t;}
ll Abs(ll x) {return (x<0)?-x:x;}

int a[maxn];

int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    //clock_t st=clock();
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;ll m;scanf("%d%lld",&n,&m);
        For(i,1,n) scanf("%d",&a[i]);
        sort(a+1,a+1+n);ll ms=0;int cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(ms>=m) break;
            while(a[i]>ms+1&&ms<m) cnt++,ms=ms*2+1;
            ms=ms+a[i];
        }
        while(ms<m) cnt++,ms=ms*2+1;
        printf("%d
",cnt);
    }
    //clock_t ed=clock();
    //printf("

Time Used : %.5lf Ms.
",(double)(ed-st)/CLOCKS_PER_SEC);
    return 0;
}
原文地址:https://www.cnblogs.com/CtrlKismet/p/7157439.html