2019牛客多校第⑨场D Knapsack Cryptosystem(折半搜索)

题意:给定大小为n(<=36)的集合a,整数s,求a的一个和为s的子集(有且只有一个)

直接搜索要\(2^{36}\)次,时间过多,考虑一次搜索前半集合,一次搜索后半集合,得到两个\(2^{16}\)的答案数组,就变成了双数组匹配问题

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll maxn=1e6+5;
struct node{
    ll vis,v;//用二进制数vis表示元素的选择情况
    node(ll a=0,ll b=0):vis(a),v(b){}
    bool operator<(node b){
        return v<b.v;
    }
}N[maxn];
ll a[40];
ll n,half,cnt;
ll s;
void dfs(ll cur,ll sum,ll vis){
    if(sum>s)return;
    if(cur==half){
        N[++cnt].vis=vis;
        N[cnt].v=sum;
        return;
    }
    if(a[cur]+sum<=s) dfs(cur+1,sum+a[cur],vis|1<<(cur-1));
    dfs(cur+1,sum,vis);
}
void dfs_(ll cur,ll sum,ll vis){
    if(sum>s) return;
    if(cur==n+1){
        ll temp=s-sum;
        ll p=lower_bound(N+1,N+1+cnt,node(1,temp) )-N;
        if(N[p].v==temp){
            ll t=N[p].vis;
            for(int i=1;i<half;i++){
                if(t&1) printf("1");
                else printf("0");
                t=t>>1;
            }
            t=vis;
            for(int i=half;i<=n;i++){
                if(t&1) printf("1");
                else printf("0");
                t=t>>1;
            }
            printf("\n");
            exit(0);
        }
        return;
    }
    if(sum+a[cur]<=s) dfs_(cur+1,sum+a[cur],vis|1<<(cur-half));
    dfs_(cur+1,sum,vis);
}
int main(){
    cin>>n>>s;
    half=n/2;
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    dfs(1,0,0);
    sort(N+1,N+1+cnt);
    dfs_(half,0,0);
}

猜你喜欢

转载自www.cnblogs.com/ucprer/p/11359864.html