子集(状态压缩)(meet in the middle)

子集

【问题描述】

R 君得到了?个集合,???共有 n 个正整数。
R 君对这个集合很感兴趣。 R 君通过努?钻研,发现了这个集合?共有 2n 个子集。
现在 R 君又对这个集合的?集很感兴趣。
定义?个集合的权值是这个集合内所有数字的和的话。
那么 R 君想问问你,这个集合的权值第 K小子集是多?。
ps. 涉及到较少数字的 long long 输?输出,建议使用 cin/cout。

【输入格式】

第??两个正整数 n,k。 接下来一行 n 个正整数,表?集合内元素。

【输出格式】

输出?个数字,表?集合的权值第 K 小子集的权值。

【样例输入】

2 3 1 2

【样例输出】

2
6

【数据规模及约定】

对于前 20% 的数据,1 ≤ n ≤ 15。
对于前 40% 的数据,1 ≤ n ≤ 22。
对于前 100% 的数据,1 ≤ n ≤ 35, 1 ≤ k ≤ 2n,1 ≤ 集合元素 ≤ 109。



我们考虑枚举集合中的子集,看到数据范围比较小,我们可以考虑状态压缩(其实一般范围比较小的子集或者选与不选的问题都可以用状态压缩的二进制来表示)。
然后因为两个子集取的集合合并起来必定包含原集合的所有子集,所以我们可以考虑二分,这样复杂度可以大大降低。
这样我们可以考虑两个指针,分别有一个在均分之后的集合里qwq,然后排完序之后指针的位置(答案)就拥有了单调性。
这样我们就可以遍历出位置了。
可能我说的不是很清楚,但是这种做法是有算法名称的——meet in the middle或者two pointer(就当个trick记下来吧)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;

LL n,k;
int n1,n2;

LL a[105];
LL b1[(1<<18)+5];
LL b2[(1<<18)+5];

int lowbit(int x){
    return x&-x;
}
void prepare(LL a[],LL b[],LL n)
{
    for(int i=0;i<n;i++) b[1<<i] = a[i];
    for(int i=1;i<(1<<n);i++){
        b[i]=b[i^lowbit(i)]+b[lowbit(i)];
    }
}

bool judge(LL x){
    int p1=0,p2=0;
    while(p2+1<(1<<n2) && b1[p1]+b2[p2+1]<=x)  p2++;
    LL cnt = 0;
    while(p1<(1<<n1))
    {
        while(p2>=0&&b1[p1]+b2[p2]>x) p2--;
        if(p2<0) break;
        cnt+=p2+1;
        p1++;
    }
    return cnt>=k;
}
int main(){
    //freopen("subset.in","r",stdin);
    //freopen("subset.out","w",stdout);
    cin >> n>>k;
    for(int i=0;i<n;i++) cin>>a[i];
    n1= n/2,n2 = n-n1;
    prepare(a,b1,n1);
    prepare(a+n1,b2,n2);
    sort(b1,b1+(1<<n1));
    sort(b2,b2+(1<<n2));
    LL l=-1,r=35*1LL*(int)1e9;
    while(r-l>1)
    {
        LL mid=(l+r)/2;
        if(judge(mid)) r=mid;
        else l=mid;
    }
    cout<<r<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/fengxunling/p/9738747.html

时间: 2024-10-04 16:26:49

子集(状态压缩)(meet in the middle)的相关文章

2017盛大游戏杯 零件组装(状态压缩DP之巧妙枚举子集)

题目链接:2017盛大游戏杯 零件组装 题意: 有n个零件,给你相邻关系和排斥关系,每两块零件组装起来有一个代价,问最少的代价总和是多少. 题解: 考虑状态压缩,dp[i]表示i这个集合为一个零件块. 那么要枚举一下i的子集.O(3^n). 先要预处理一下每个集合的排斥个数和相邻个数,然后容斥一下就可以了. 1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int

11825 - Hackers&#39; Crackdown 状态压缩 dp 枚举子集

11825 - Hackers' Crackdown 状态压缩 dp 枚举子集 ACM 题目地址:11825 - Hackers' Crackdown 题意: 有一个由编号0~n-1的n台计算机组成的网络,一共有n种服务,每台计算机上都运行着全部服务,对于每台计算机,你可以选择停止一项服务,这个行为会导致与这台计算机和与他相连的其他计算机上的这项服务都停止(原来已经停止的继续保持停止状态).求最多能使多少个服务瘫痪(即没有任何一台计算机在运行这项服务). 分析: 题目说白了,就是: 把n个集合p

UVA 11825 - Hackers&amp;#39; Crackdown 状态压缩 dp 枚举子集

UVA 11825 - Hackers' Crackdown 状态压缩 dp 枚举子集 ACM 题目地址:11825 - Hackers' Crackdown 题意: 有一个由编号0~n-1的n台计算机组成的网络,一共同拥有n种服务,每台计算机上都执行着所有服务,对于每台计算机,你能够选择停止一项服务,这个行为会导致与这台计算机和与他相连的其它计算机上的这项服务都停止(原来已经停止的继续保持停止状态). 求最多能使多少个服务瘫痪(即没有不论什么一台计算机在执行这项服务). 分析: 题目说白了.就

UVA 11825 状态压缩DP+子集思想

很明显的状态压缩思想了.把全集分组,枚举每个集合的子集,看一个子集是否能覆盖所有的点,若能,则f[s]=max(f[s],f[s^s0]+1).即与差集+1比较. 这种枚举集合的思想还是第一次遇到,果然太弱了....~~~~ 其中枚举集合 for(s0=s;s0;s0=(s0-1)&s) #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> usin

状态压缩子集问题

描述:给定一个n(1≤n≤10)个数(可正可负)的集合,求一个划分方法,使得所有划分块的代价和最小.其中每个分块的代价和最小.其中每个块的代价为块内数字的和的平方. 分析:因为看到n最大为10,所以可以用状态压缩DP,复杂度最高为O(2^10*2^10) 设dp[i]表示状态为i的时候的最小代价和. 可以推出dp[X] = min(dp[Y] + dp[Z] | Y∪Z = X && Y∩Z = ∅} 初始的时候dp[X] = sum{a[i] | i在X集合中}^2. 所以可以写出 1

1252 - Twenty Questions(状态压缩DP)

经典的状态压缩DP .  有没有感觉这道题和什么东西有点像?  没错,是01背包 . 将特征看作物品 , 只不过这里的状态有点复杂, 需要用一个集合才能表示它, 所以我们用d[s][a]来表示,已经询问了特征集s , 假设我们要猜的物品是w ,w所具备的特征集为a ,此时还要询问的最小次数 .   显然a是s的子集,而且要注意本题的要求, 求的是最小化的最大询问次数 .也就是说无论猜哪个物品,猜这么多次一定能猜到 . 那么状态如何转移呢? 就像背包问题,对于一个特征k ,我们要抉择:要k还是不要

Bear and Floodlight 状态压缩DP啊

Bear and Floodlight Time Limit: 4000MS   Memory Limit: 262144KB   64bit IO Format: %I64d & %I64u [Submit]   [Go Back]   [Status] Description One day a bear lived on the Oxy axis. He was afraid of the dark, so he couldn't move at night along the plane

hdu 3217 Health(状态压缩DP)

Health Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 527    Accepted Submission(s): 145 Problem Description Unfortunately YY gets ill, but he does not want to go to hospital. His girlfriend LM

BZOJ 2734 集合选数(状态压缩DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2734 题意:给出一个由1到n的数字组成的集合.定义合法子集为若x在子集中则2x.3x均不能在子集中.求有多少个合法的子集. 思路: 1   3    9 2   6    12 4   12   36 对于上面的矩阵,我们发现就等价于不选相邻数字的方案数.因此枚举每个还没有用到的数字,建立以该数字为左上角的矩阵.接着就是状态压缩DP. int a[N][N]; i64 f[2][1<<